diff options
| author | 2012-04-27 01:39:59 -0700 | |
|---|---|---|
| committer | 2012-04-27 01:39:59 -0700 | |
| commit | c6911d47d193d76a40938f56854ca06c00edab68 (patch) | |
| tree | bdbbe67865eb09ea78b3d22ed7a4c330afa472b6 | |
| parent | b33c34cc25263647e0270ab92cb7fdb5af79416c (diff) | |
| parent | 99c12e8d5f12f239c28644a837937810299e5e3f (diff) | |
Merge "Create WebViewDatabaseClassic from WebViewDatabase." into jb-dev
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 11 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewClassic.java | 12 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewDatabase.java | 596 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewDatabaseClassic.java | 624 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewFactoryProvider.java | 19 |
5 files changed, 685 insertions, 577 deletions
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 7b6b54cce0cf..c169de4cd69a 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -72,7 +72,7 @@ class BrowserFrame extends Handler { private final CallbackProxy mCallbackProxy; private final WebSettingsClassic mSettings; private final Context mContext; - private final WebViewDatabase mDatabase; + private final WebViewDatabaseClassic mDatabase; private final WebViewCore mWebViewCore; /* package */ boolean mLoadInitFromJava; private int mLoadType; @@ -241,7 +241,7 @@ class BrowserFrame extends Handler { mSettings = settings; mContext = context; mCallbackProxy = proxy; - mDatabase = WebViewDatabase.getInstance(appContext); + mDatabase = WebViewDatabaseClassic.getInstance(appContext); mWebViewCore = w; mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy); @@ -496,8 +496,8 @@ class BrowserFrame extends Handler { if (item != null) { WebAddress uri = new WebAddress(item.getUrl()); String schemePlusHost = uri.getScheme() + uri.getHost(); - String[] up = - mDatabase.getUsernamePassword(schemePlusHost); + String[] up = mDatabase.getUsernamePassword( + schemePlusHost); if (up != null && up[0] != null) { setUsernamePassword(up[0], up[1]); } @@ -809,8 +809,7 @@ class BrowserFrame extends Handler { // non-null username implies that user has // chosen to save password, so update the // recorded password - mDatabase.setUsernamePassword( - schemePlusHost, username, password); + mDatabase.setUsernamePassword(schemePlusHost, username, password); } } else { // CallbackProxy will handle creating the resume diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 9cf0d54d3b68..ca17d31b7b32 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -701,7 +701,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // A final CallbackProxy shared by WebViewCore and BrowserFrame. private CallbackProxy mCallbackProxy; - private WebViewDatabase mDatabase; + private WebViewDatabaseClassic mDatabase; // SSL certificate for the main top-level page (if secure) private SslCertificate mCertificate; @@ -1230,7 +1230,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mViewManager = new ViewManager(this); L10nUtils.setApplicationContext(context.getApplicationContext()); mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces); - mDatabase = WebViewDatabase.getInstance(context); + mDatabase = WebViewDatabaseClassic.getInstance(context); mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel mZoomManager = new ZoomManager(this, mCallbackProxy); @@ -1294,6 +1294,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc public WebStorage getWebStorage() { return WebStorageClassic.getInstance(); } + + @Override + public WebViewDatabase getWebViewDatabase(Context context) { + return WebViewDatabaseClassic.getInstance(context); + } } private void onHandleUiEvent(MotionEvent event, int eventType, int flags) { @@ -7192,8 +7197,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc break; } case NEVER_REMEMBER_PASSWORD: { - mDatabase.setUsernamePassword( - msg.getData().getString("host"), null, null); + mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null); ((Message) msg.obj).sendToTarget(); break; } diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 6c35f199bb5d..9d10d67be0f7 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -16,611 +16,79 @@ package android.webkit; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - -import android.content.ContentValues; import android.content.Context; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteStatement; -import android.util.Log; +/** + * This class allows developers to determine whether any WebView used in the + * application has stored any of the following types of browsing data and + * to clear any such stored data for all WebViews in the application. + * <ul> + * <li>Username/password pairs entered into web forms</li> + * <li>HTTP authentication username/password pairs</li> + * <li>Data entered into text fields (e.g. for autocomplete suggestions)</li> + * </ul> + */ public class WebViewDatabase { - private static final String DATABASE_FILE = "webview.db"; - private static final String CACHE_DATABASE_FILE = "webviewCache.db"; - - // log tag + // TODO: deprecate/hide this. protected static final String LOGTAG = "webviewdatabase"; - private static final int DATABASE_VERSION = 11; - // 2 -> 3 Modified Cache table to allow cache of redirects - // 3 -> 4 Added Oma-Downloads table - // 4 -> 5 Modified Cache table to support persistent contentLength - // 5 -> 4 Removed Oma-Downoads table - // 5 -> 6 Add INDEX for cache table - // 6 -> 7 Change cache localPath from int to String - // 7 -> 8 Move cache to its own db - // 8 -> 9 Store both scheme and host when storing passwords - // 9 -> 10 Update httpauth table UNIQUE - // 10 -> 11 Drop cookies and cache now managed by the chromium stack, - // and update the form data table to use the new format - // implemented for b/5265606. - - private static WebViewDatabase mInstance = null; - - private static SQLiteDatabase mDatabase = null; - - // synchronize locks - private final Object mPasswordLock = new Object(); - private final Object mFormLock = new Object(); - private final Object mHttpAuthLock = new Object(); - - private static final String mTableNames[] = { - "password", "formurl", "formdata", "httpauth" - }; - - // Table ids (they are index to mTableNames) - private static final int TABLE_PASSWORD_ID = 0; - private static final int TABLE_FORMURL_ID = 1; - private static final int TABLE_FORMDATA_ID = 2; - private static final int TABLE_HTTPAUTH_ID = 3; - - // column id strings for "_id" which can be used by any table - private static final String ID_COL = "_id"; - - private static final String[] ID_PROJECTION = new String[] { - "_id" - }; - - // column id strings for "password" table - private static final String PASSWORD_HOST_COL = "host"; - private static final String PASSWORD_USERNAME_COL = "username"; - private static final String PASSWORD_PASSWORD_COL = "password"; - - // column id strings for "formurl" table - private static final String FORMURL_URL_COL = "url"; - - // column id strings for "formdata" table - private static final String FORMDATA_URLID_COL = "urlid"; - private static final String FORMDATA_NAME_COL = "name"; - private static final String FORMDATA_VALUE_COL = "value"; - - // column id strings for "httpauth" table - private static final String HTTPAUTH_HOST_COL = "host"; - private static final String HTTPAUTH_REALM_COL = "realm"; - private static final String HTTPAUTH_USERNAME_COL = "username"; - private static final String HTTPAUTH_PASSWORD_COL = "password"; - - // Initially true until the background thread completes. - private boolean mInitialized = false; - - private WebViewDatabase(final Context context) { - new Thread() { - @Override - public void run() { - init(context); - } - }.start(); - - // Singleton only, use getInstance() - } - - public static synchronized WebViewDatabase getInstance(Context context) { - if (mInstance == null) { - mInstance = new WebViewDatabase(context); - } - return mInstance; - } - - private synchronized void init(Context context) { - if (mInitialized) { - return; - } - - initDatabase(context); - // Before using the Chromium HTTP stack, we stored the WebKit cache in - // our own DB. Clean up the DB file if it's still around. - context.deleteDatabase(CACHE_DATABASE_FILE); - - // Thread done, notify. - mInitialized = true; - notify(); - } - - private void initDatabase(Context context) { - try { - mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null); - } catch (SQLiteException e) { - // try again by deleting the old db and create a new one - if (context.deleteDatabase(DATABASE_FILE)) { - mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, - null); - } - } - - // mDatabase should not be null, - // the only case is RequestAPI test has problem to create db - if (mDatabase == null) { - mInitialized = true; - notify(); - return; - } - - if (mDatabase.getVersion() != DATABASE_VERSION) { - mDatabase.beginTransactionNonExclusive(); - try { - upgradeDatabase(); - mDatabase.setTransactionSuccessful(); - } finally { - mDatabase.endTransaction(); - } - } - } - - private static void upgradeDatabase() { - upgradeDatabaseToV10(); - upgradeDatabaseFromV10ToV11(); - // Add future database upgrade functions here, one version at a - // time. - mDatabase.setVersion(DATABASE_VERSION); - } - - private static void upgradeDatabaseFromV10ToV11() { - int oldVersion = mDatabase.getVersion(); - - if (oldVersion >= 11) { - // Nothing to do. - return; - } - - // Clear out old java stack cookies - this data is now stored in - // a separate database managed by the Chrome stack. - mDatabase.execSQL("DROP TABLE IF EXISTS cookies"); - - // Likewise for the old cache table. - mDatabase.execSQL("DROP TABLE IF EXISTS cache"); - - // Update form autocomplete URLs to match new ICS formatting. - Cursor c = mDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null, - null, null, null, null); - while (c.moveToNext()) { - String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL))); - String url = c.getString(c.getColumnIndex(FORMURL_URL_COL)); - ContentValues cv = new ContentValues(1); - cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url)); - mDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?", - new String[] { urlId }); - } - c.close(); - } - - private static void upgradeDatabaseToV10() { - int oldVersion = mDatabase.getVersion(); - - if (oldVersion >= 10) { - // Nothing to do. - return; - } - - if (oldVersion != 0) { - Log.i(LOGTAG, "Upgrading database from version " - + oldVersion + " to " - + DATABASE_VERSION + ", which will destroy old data"); - } - - if (9 == oldVersion) { - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_HTTPAUTH_ID]); - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL - + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " - + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" - + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL - + ") ON CONFLICT REPLACE);"); - return; - } - - mDatabase.execSQL("DROP TABLE IF EXISTS cookies"); - mDatabase.execSQL("DROP TABLE IF EXISTS cache"); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMURL_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMDATA_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_HTTPAUTH_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_PASSWORD_ID]); - - // formurl - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL - + " TEXT" + ");"); - - // formdata - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL - + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" - + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " - + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); - - // httpauth - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL - + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " - + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" - + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL - + ") ON CONFLICT REPLACE);"); - // passwords - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL - + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE (" - + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL - + ") ON CONFLICT REPLACE);"); - } - - // Wait for the background initialization thread to complete and check the - // database creation status. - private boolean checkInitialized() { - synchronized (this) { - while (!mInitialized) { - try { - wait(); - } catch (InterruptedException e) { - Log.e(LOGTAG, "Caught exception while checking " + - "initialization"); - Log.e(LOGTAG, Log.getStackTraceString(e)); - } - } - } - return mDatabase != null; - } - - private boolean hasEntries(int tableId) { - if (!checkInitialized()) { - return false; - } - - Cursor cursor = null; - boolean ret = false; - try { - cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION, - null, null, null, null, null); - ret = cursor.moveToFirst() == true; - } catch (IllegalStateException e) { - Log.e(LOGTAG, "hasEntries", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - - // - // password functions - // - /** - * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique. - * - * @param schemePlusHost The scheme and host for the password - * @param username The username for the password. If it is null, it means - * password can't be saved. - * @param password The password + * @hide Only for use by WebViewProvider implementations. */ - void setUsernamePassword(String schemePlusHost, String username, - String password) { - if (schemePlusHost == null || !checkInitialized()) { - return; - } - - synchronized (mPasswordLock) { - final ContentValues c = new ContentValues(); - c.put(PASSWORD_HOST_COL, schemePlusHost); - c.put(PASSWORD_USERNAME_COL, username); - c.put(PASSWORD_PASSWORD_COL, password); - mDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL, - c); - } + protected WebViewDatabase() { } - /** - * Retrieve the username and password for a given host - * - * @param schemePlusHost The scheme and host which passwords applies to - * @return String[] if found, String[0] is username, which can be null and - * String[1] is password. Return null if it can't find anything. - */ - String[] getUsernamePassword(String schemePlusHost) { - if (schemePlusHost == null || !checkInitialized()) { - return null; - } - - final String[] columns = new String[] { - PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL - }; - final String selection = "(" + PASSWORD_HOST_COL + " == ?)"; - synchronized (mPasswordLock) { - String[] ret = null; - Cursor cursor = null; - try { - cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID], - columns, selection, new String[] { schemePlusHost }, null, - null, null); - if (cursor.moveToFirst()) { - ret = new String[2]; - ret[0] = cursor.getString( - cursor.getColumnIndex(PASSWORD_USERNAME_COL)); - ret[1] = cursor.getString( - cursor.getColumnIndex(PASSWORD_PASSWORD_COL)); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getUsernamePassword", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } + public static synchronized WebViewDatabase getInstance(Context context) { + return WebViewFactory.getProvider().getWebViewDatabase(context); } /** - * Find out if there are any passwords saved. + * Gets whether there are any username/password combinations + * from web pages saved. * - * @return TRUE if there is passwords saved + * @return true if there are any username/passwords used in web + * forms saved */ public boolean hasUsernamePassword() { - synchronized (mPasswordLock) { - return hasEntries(TABLE_PASSWORD_ID); - } + throw new MustOverrideException(); } /** - * Clear password database + * Clears any username/password combinations saved from web forms. */ public void clearUsernamePassword() { - if (!checkInitialized()) { - return; - } - - synchronized (mPasswordLock) { - mDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null); - } - } - - // - // http authentication password functions - // - - /** - * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, - * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique. - * - * @param host The host for the password - * @param realm The realm for the password - * @param username The username for the password. If it is null, it means - * password can't be saved. - * @param password The password - */ - void setHttpAuthUsernamePassword(String host, String realm, String username, - String password) { - if (host == null || realm == null || !checkInitialized()) { - return; - } - - synchronized (mHttpAuthLock) { - final ContentValues c = new ContentValues(); - c.put(HTTPAUTH_HOST_COL, host); - c.put(HTTPAUTH_REALM_COL, realm); - c.put(HTTPAUTH_USERNAME_COL, username); - c.put(HTTPAUTH_PASSWORD_COL, password); - mDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL, - c); - } + throw new MustOverrideException(); } /** - * Retrieve the HTTP authentication username and password for a given - * host+realm pair + * Gets whether there are any HTTP authentication username/password combinations saved. * - * @param host The host the password applies to - * @param realm The realm the password applies to - * @return String[] if found, String[0] is username, which can be null and - * String[1] is password. Return null if it can't find anything. - */ - String[] getHttpAuthUsernamePassword(String host, String realm) { - if (host == null || realm == null || !checkInitialized()){ - return null; - } - - final String[] columns = new String[] { - HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL - }; - final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND (" - + HTTPAUTH_REALM_COL + " == ?)"; - synchronized (mHttpAuthLock) { - String[] ret = null; - Cursor cursor = null; - try { - cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID], - columns, selection, new String[] { host, realm }, null, - null, null); - if (cursor.moveToFirst()) { - ret = new String[2]; - ret[0] = cursor.getString( - cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)); - ret[1] = cursor.getString( - cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getHttpAuthUsernamePassword", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - } - - /** - * Find out if there are any HTTP authentication passwords saved. - * - * @return TRUE if there are passwords saved + * @return true if there are any HTTP authentication username/passwords saved */ public boolean hasHttpAuthUsernamePassword() { - synchronized (mHttpAuthLock) { - return hasEntries(TABLE_HTTPAUTH_ID); - } + throw new MustOverrideException(); } /** - * Clear HTTP authentication password database + * Clears any HTTP authentication username/passwords that are saved. */ public void clearHttpAuthUsernamePassword() { - if (!checkInitialized()) { - return; - } - - synchronized (mHttpAuthLock) { - mDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null); - } - } - - // - // form data functions - // - - /** - * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL, - * FORMDATA_VALUE_COL) is unique - * - * @param url The url of the site - * @param formdata The form data in HashMap - */ - void setFormData(String url, HashMap<String, String> formdata) { - if (url == null || formdata == null || !checkInitialized()) { - return; - } - - final String selection = "(" + FORMURL_URL_COL + " == ?)"; - synchronized (mFormLock) { - long urlid = -1; - Cursor cursor = null; - try { - cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID], - ID_PROJECTION, selection, new String[] { url }, null, null, - null); - if (cursor.moveToFirst()) { - urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); - } else { - ContentValues c = new ContentValues(); - c.put(FORMURL_URL_COL, url); - urlid = mDatabase.insert( - mTableNames[TABLE_FORMURL_ID], null, c); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "setFormData", e); - } finally { - if (cursor != null) cursor.close(); - } - if (urlid >= 0) { - Set<Entry<String, String>> set = formdata.entrySet(); - Iterator<Entry<String, String>> iter = set.iterator(); - ContentValues map = new ContentValues(); - map.put(FORMDATA_URLID_COL, urlid); - while (iter.hasNext()) { - Entry<String, String> entry = iter.next(); - map.put(FORMDATA_NAME_COL, entry.getKey()); - map.put(FORMDATA_VALUE_COL, entry.getValue()); - mDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map); - } - } - } + throw new MustOverrideException(); } /** - * Get all the values for a form entry with "name" in a given site + * Gets whether there is any previously-entered form data saved. * - * @param url The url of the site - * @param name The name of the form entry - * @return A list of values. Return empty list if nothing is found. - */ - ArrayList<String> getFormData(String url, String name) { - ArrayList<String> values = new ArrayList<String>(); - if (url == null || name == null || !checkInitialized()) { - return values; - } - - final String urlSelection = "(" + FORMURL_URL_COL + " == ?)"; - final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND (" - + FORMDATA_NAME_COL + " == ?)"; - synchronized (mFormLock) { - Cursor cursor = null; - try { - cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID], - ID_PROJECTION, urlSelection, new String[] { url }, null, - null, null); - while (cursor.moveToNext()) { - long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); - Cursor dataCursor = null; - try { - dataCursor = mDatabase.query( - mTableNames[TABLE_FORMDATA_ID], - new String[] { ID_COL, FORMDATA_VALUE_COL }, - dataSelection, - new String[] { Long.toString(urlid), name }, - null, null, null); - if (dataCursor.moveToFirst()) { - int valueCol = dataCursor.getColumnIndex( - FORMDATA_VALUE_COL); - do { - values.add(dataCursor.getString(valueCol)); - } while (dataCursor.moveToNext()); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getFormData dataCursor", e); - } finally { - if (dataCursor != null) dataCursor.close(); - } - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getFormData cursor", e); - } finally { - if (cursor != null) cursor.close(); - } - return values; - } - } - - /** - * Find out if there is form data saved. - * - * @return TRUE if there is form data in the database + * @return true if there is form data saved */ public boolean hasFormData() { - synchronized (mFormLock) { - return hasEntries(TABLE_FORMURL_ID); - } + throw new MustOverrideException(); } /** - * Clear form database + * Clears any stored previously-entered form data. */ public void clearFormData() { - if (!checkInitialized()) { - return; - } - - synchronized (mFormLock) { - mDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null); - mDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null); - } + throw new MustOverrideException(); } } diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java new file mode 100644 index 000000000000..9b1d4cb92986 --- /dev/null +++ b/core/java/android/webkit/WebViewDatabaseClassic.java @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteStatement; +import android.util.Log; + +final class WebViewDatabaseClassic extends WebViewDatabase { + private static final String LOGTAG = "WebViewDatabaseClassic"; + private static final String DATABASE_FILE = "webview.db"; + private static final String CACHE_DATABASE_FILE = "webviewCache.db"; + + private static final int DATABASE_VERSION = 11; + // 2 -> 3 Modified Cache table to allow cache of redirects + // 3 -> 4 Added Oma-Downloads table + // 4 -> 5 Modified Cache table to support persistent contentLength + // 5 -> 4 Removed Oma-Downoads table + // 5 -> 6 Add INDEX for cache table + // 6 -> 7 Change cache localPath from int to String + // 7 -> 8 Move cache to its own db + // 8 -> 9 Store both scheme and host when storing passwords + // 9 -> 10 Update httpauth table UNIQUE + // 10 -> 11 Drop cookies and cache now managed by the chromium stack, + // and update the form data table to use the new format + // implemented for b/5265606. + + private static WebViewDatabaseClassic sInstance = null; + + private static SQLiteDatabase sDatabase = null; + + // synchronize locks + private final Object mPasswordLock = new Object(); + private final Object mFormLock = new Object(); + private final Object mHttpAuthLock = new Object(); + + private static final String mTableNames[] = { + "password", "formurl", "formdata", "httpauth" + }; + + // Table ids (they are index to mTableNames) + private static final int TABLE_PASSWORD_ID = 0; + private static final int TABLE_FORMURL_ID = 1; + private static final int TABLE_FORMDATA_ID = 2; + private static final int TABLE_HTTPAUTH_ID = 3; + + // column id strings for "_id" which can be used by any table + private static final String ID_COL = "_id"; + + private static final String[] ID_PROJECTION = new String[] { + "_id" + }; + + // column id strings for "password" table + private static final String PASSWORD_HOST_COL = "host"; + private static final String PASSWORD_USERNAME_COL = "username"; + private static final String PASSWORD_PASSWORD_COL = "password"; + + // column id strings for "formurl" table + private static final String FORMURL_URL_COL = "url"; + + // column id strings for "formdata" table + private static final String FORMDATA_URLID_COL = "urlid"; + private static final String FORMDATA_NAME_COL = "name"; + private static final String FORMDATA_VALUE_COL = "value"; + + // column id strings for "httpauth" table + private static final String HTTPAUTH_HOST_COL = "host"; + private static final String HTTPAUTH_REALM_COL = "realm"; + private static final String HTTPAUTH_USERNAME_COL = "username"; + private static final String HTTPAUTH_PASSWORD_COL = "password"; + + // Initially true until the background thread completes. + private boolean mInitialized = false; + + WebViewDatabaseClassic(final Context context) { + new Thread() { + @Override + public void run() { + init(context); + } + }.start(); + + // Singleton only, use getInstance() + } + + public static synchronized WebViewDatabaseClassic getInstance(Context context) { + if (sInstance == null) { + sInstance = new WebViewDatabaseClassic(context); + } + return sInstance; + } + + private synchronized void init(Context context) { + if (mInitialized) { + return; + } + + initDatabase(context); + // Before using the Chromium HTTP stack, we stored the WebKit cache in + // our own DB. Clean up the DB file if it's still around. + context.deleteDatabase(CACHE_DATABASE_FILE); + + // Thread done, notify. + mInitialized = true; + notify(); + } + + private void initDatabase(Context context) { + try { + sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null); + } catch (SQLiteException e) { + // try again by deleting the old db and create a new one + if (context.deleteDatabase(DATABASE_FILE)) { + sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, + null); + } + } + + // sDatabase should not be null, + // the only case is RequestAPI test has problem to create db + if (sDatabase == null) { + mInitialized = true; + notify(); + return; + } + + if (sDatabase.getVersion() != DATABASE_VERSION) { + sDatabase.beginTransactionNonExclusive(); + try { + upgradeDatabase(); + sDatabase.setTransactionSuccessful(); + } finally { + sDatabase.endTransaction(); + } + } + } + + private static void upgradeDatabase() { + upgradeDatabaseToV10(); + upgradeDatabaseFromV10ToV11(); + // Add future database upgrade functions here, one version at a + // time. + sDatabase.setVersion(DATABASE_VERSION); + } + + private static void upgradeDatabaseFromV10ToV11() { + int oldVersion = sDatabase.getVersion(); + + if (oldVersion >= 11) { + // Nothing to do. + return; + } + + // Clear out old java stack cookies - this data is now stored in + // a separate database managed by the Chrome stack. + sDatabase.execSQL("DROP TABLE IF EXISTS cookies"); + + // Likewise for the old cache table. + sDatabase.execSQL("DROP TABLE IF EXISTS cache"); + + // Update form autocomplete URLs to match new ICS formatting. + Cursor c = sDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null, + null, null, null, null); + while (c.moveToNext()) { + String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL))); + String url = c.getString(c.getColumnIndex(FORMURL_URL_COL)); + ContentValues cv = new ContentValues(1); + cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url)); + sDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?", + new String[] { urlId }); + } + c.close(); + } + + private static void upgradeDatabaseToV10() { + int oldVersion = sDatabase.getVersion(); + + if (oldVersion >= 10) { + // Nothing to do. + return; + } + + if (oldVersion != 0) { + Log.i(LOGTAG, "Upgrading database from version " + + oldVersion + " to " + + DATABASE_VERSION + ", which will destroy old data"); + } + + if (9 == oldVersion) { + sDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_HTTPAUTH_ID]); + sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL + + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " + + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" + + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL + + ") ON CONFLICT REPLACE);"); + return; + } + + sDatabase.execSQL("DROP TABLE IF EXISTS cookies"); + sDatabase.execSQL("DROP TABLE IF EXISTS cache"); + sDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_FORMURL_ID]); + sDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_FORMDATA_ID]); + sDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_HTTPAUTH_ID]); + sDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_PASSWORD_ID]); + + // formurl + sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL + + " TEXT" + ");"); + + // formdata + sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL + + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" + + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " + + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); + + // httpauth + sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL + + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " + + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" + + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL + + ") ON CONFLICT REPLACE);"); + // passwords + sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL + + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE (" + + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL + + ") ON CONFLICT REPLACE);"); + } + + // Wait for the background initialization thread to complete and check the + // database creation status. + private boolean checkInitialized() { + synchronized (this) { + while (!mInitialized) { + try { + wait(); + } catch (InterruptedException e) { + Log.e(LOGTAG, "Caught exception while checking " + + "initialization"); + Log.e(LOGTAG, Log.getStackTraceString(e)); + } + } + } + return sDatabase != null; + } + + private boolean hasEntries(int tableId) { + if (!checkInitialized()) { + return false; + } + + Cursor cursor = null; + boolean ret = false; + try { + cursor = sDatabase.query(mTableNames[tableId], ID_PROJECTION, + null, null, null, null, null); + ret = cursor.moveToFirst() == true; + } catch (IllegalStateException e) { + Log.e(LOGTAG, "hasEntries", e); + } finally { + if (cursor != null) cursor.close(); + } + return ret; + } + + // + // password functions + // + + /** + * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique. + * + * @param schemePlusHost The scheme and host for the password + * @param username The username for the password. If it is null, it means + * password can't be saved. + * @param password The password + */ + void setUsernamePassword(String schemePlusHost, String username, + String password) { + if (schemePlusHost == null || !checkInitialized()) { + return; + } + + synchronized (mPasswordLock) { + final ContentValues c = new ContentValues(); + c.put(PASSWORD_HOST_COL, schemePlusHost); + c.put(PASSWORD_USERNAME_COL, username); + c.put(PASSWORD_PASSWORD_COL, password); + sDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL, + c); + } + } + + /** + * Retrieve the username and password for a given host + * + * @param schemePlusHost The scheme and host which passwords applies to + * @return String[] if found, String[0] is username, which can be null and + * String[1] is password. Return null if it can't find anything. + */ + String[] getUsernamePassword(String schemePlusHost) { + if (schemePlusHost == null || !checkInitialized()) { + return null; + } + + final String[] columns = new String[] { + PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL + }; + final String selection = "(" + PASSWORD_HOST_COL + " == ?)"; + synchronized (mPasswordLock) { + String[] ret = null; + Cursor cursor = null; + try { + cursor = sDatabase.query(mTableNames[TABLE_PASSWORD_ID], + columns, selection, new String[] { schemePlusHost }, null, + null, null); + if (cursor.moveToFirst()) { + ret = new String[2]; + ret[0] = cursor.getString( + cursor.getColumnIndex(PASSWORD_USERNAME_COL)); + ret[1] = cursor.getString( + cursor.getColumnIndex(PASSWORD_PASSWORD_COL)); + } + } catch (IllegalStateException e) { + Log.e(LOGTAG, "getUsernamePassword", e); + } finally { + if (cursor != null) cursor.close(); + } + return ret; + } + } + + /** + * @see WebViewDatabase#hasUsernamePassword + */ + @Override + public boolean hasUsernamePassword() { + synchronized (mPasswordLock) { + return hasEntries(TABLE_PASSWORD_ID); + } + } + + /** + * @see WebViewDatabase#clearUsernamePassword + */ + @Override + public void clearUsernamePassword() { + if (!checkInitialized()) { + return; + } + + synchronized (mPasswordLock) { + sDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null); + } + } + + // + // http authentication password functions + // + + /** + * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, + * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique. + * + * @param host The host for the password + * @param realm The realm for the password + * @param username The username for the password. If it is null, it means + * password can't be saved. + * @param password The password + */ + void setHttpAuthUsernamePassword(String host, String realm, String username, + String password) { + if (host == null || realm == null || !checkInitialized()) { + return; + } + + synchronized (mHttpAuthLock) { + final ContentValues c = new ContentValues(); + c.put(HTTPAUTH_HOST_COL, host); + c.put(HTTPAUTH_REALM_COL, realm); + c.put(HTTPAUTH_USERNAME_COL, username); + c.put(HTTPAUTH_PASSWORD_COL, password); + sDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL, + c); + } + } + + /** + * Retrieve the HTTP authentication username and password for a given + * host+realm pair + * + * @param host The host the password applies to + * @param realm The realm the password applies to + * @return String[] if found, String[0] is username, which can be null and + * String[1] is password. Return null if it can't find anything. + */ + String[] getHttpAuthUsernamePassword(String host, String realm) { + if (host == null || realm == null || !checkInitialized()){ + return null; + } + + final String[] columns = new String[] { + HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL + }; + final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND (" + + HTTPAUTH_REALM_COL + " == ?)"; + synchronized (mHttpAuthLock) { + String[] ret = null; + Cursor cursor = null; + try { + cursor = sDatabase.query(mTableNames[TABLE_HTTPAUTH_ID], + columns, selection, new String[] { host, realm }, null, + null, null); + if (cursor.moveToFirst()) { + ret = new String[2]; + ret[0] = cursor.getString( + cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)); + ret[1] = cursor.getString( + cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)); + } + } catch (IllegalStateException e) { + Log.e(LOGTAG, "getHttpAuthUsernamePassword", e); + } finally { + if (cursor != null) cursor.close(); + } + return ret; + } + } + + /** + * @see WebViewDatabase#hasHttpAuthUsernamePassword + */ + @Override + public boolean hasHttpAuthUsernamePassword() { + synchronized (mHttpAuthLock) { + return hasEntries(TABLE_HTTPAUTH_ID); + } + } + + /** + * @see WebViewDatabase#clearHttpAuthUsernamePassword + */ + @Override + public void clearHttpAuthUsernamePassword() { + if (!checkInitialized()) { + return; + } + + synchronized (mHttpAuthLock) { + sDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null); + } + } + + // + // form data functions + // + + /** + * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL, + * FORMDATA_VALUE_COL) is unique + * + * @param url The url of the site + * @param formdata The form data in HashMap + */ + void setFormData(String url, HashMap<String, String> formdata) { + if (url == null || formdata == null || !checkInitialized()) { + return; + } + + final String selection = "(" + FORMURL_URL_COL + " == ?)"; + synchronized (mFormLock) { + long urlid = -1; + Cursor cursor = null; + try { + cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID], + ID_PROJECTION, selection, new String[] { url }, null, null, + null); + if (cursor.moveToFirst()) { + urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); + } else { + ContentValues c = new ContentValues(); + c.put(FORMURL_URL_COL, url); + urlid = sDatabase.insert( + mTableNames[TABLE_FORMURL_ID], null, c); + } + } catch (IllegalStateException e) { + Log.e(LOGTAG, "setFormData", e); + } finally { + if (cursor != null) cursor.close(); + } + if (urlid >= 0) { + Set<Entry<String, String>> set = formdata.entrySet(); + Iterator<Entry<String, String>> iter = set.iterator(); + ContentValues map = new ContentValues(); + map.put(FORMDATA_URLID_COL, urlid); + while (iter.hasNext()) { + Entry<String, String> entry = iter.next(); + map.put(FORMDATA_NAME_COL, entry.getKey()); + map.put(FORMDATA_VALUE_COL, entry.getValue()); + sDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map); + } + } + } + } + + /** + * Get all the values for a form entry with "name" in a given site + * + * @param url The url of the site + * @param name The name of the form entry + * @return A list of values. Return empty list if nothing is found. + */ + ArrayList<String> getFormData(String url, String name) { + ArrayList<String> values = new ArrayList<String>(); + if (url == null || name == null || !checkInitialized()) { + return values; + } + + final String urlSelection = "(" + FORMURL_URL_COL + " == ?)"; + final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND (" + + FORMDATA_NAME_COL + " == ?)"; + synchronized (mFormLock) { + Cursor cursor = null; + try { + cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID], + ID_PROJECTION, urlSelection, new String[] { url }, null, + null, null); + while (cursor.moveToNext()) { + long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); + Cursor dataCursor = null; + try { + dataCursor = sDatabase.query( + mTableNames[TABLE_FORMDATA_ID], + new String[] { ID_COL, FORMDATA_VALUE_COL }, + dataSelection, + new String[] { Long.toString(urlid), name }, + null, null, null); + if (dataCursor.moveToFirst()) { + int valueCol = dataCursor.getColumnIndex( + FORMDATA_VALUE_COL); + do { + values.add(dataCursor.getString(valueCol)); + } while (dataCursor.moveToNext()); + } + } catch (IllegalStateException e) { + Log.e(LOGTAG, "getFormData dataCursor", e); + } finally { + if (dataCursor != null) dataCursor.close(); + } + } + } catch (IllegalStateException e) { + Log.e(LOGTAG, "getFormData cursor", e); + } finally { + if (cursor != null) cursor.close(); + } + return values; + } + } + + /** + * @see WebViewDatabase#hasFormData + */ + @Override + public boolean hasFormData() { + synchronized (mFormLock) { + return hasEntries(TABLE_FORMURL_ID); + } + } + + /** + * @see WebViewDatabase#clearFormData + */ + @Override + public void clearFormData() { + if (!checkInitialized()) { + return; + } + + synchronized (mFormLock) { + sDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null); + sDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null); + } + } +} diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index a832b0ae8075..1d302f1983a0 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -16,6 +16,8 @@ package android.webkit; +import android.content.Context; + /** * This is the main entry-point into the WebView back end implementations, which the WebView * proxy class uses to instantiate all the other objects as needed. The backend must provide an @@ -63,21 +65,32 @@ public interface WebViewFactoryProvider { /** * Gets the singleton CookieManager instance for this WebView implementation. The * implementation must return the same instance on subsequent calls. - * @return the singleton CookieManager instance. + * + * @return the singleton CookieManager instance */ CookieManager getCookieManager(); /** * Gets the singleton WebIconDatabase instance for this WebView implementation. The * implementation must return the same instance on subsequent calls. - * @return the singleton WebIconDatabase instance. + * + * @return the singleton WebIconDatabase instance */ WebIconDatabase getWebIconDatabase(); /** * Gets the singleton WebStorage instance for this WebView implementation. The * implementation must return the same instance on subsequent calls. - * @return the singleton WebStorage instance. + * + * @return the singleton WebStorage instance */ WebStorage getWebStorage(); + + /** + * Gets the singleton WebViewDatabase instance for this WebView implementation. The + * implementation must return the same instance on subsequent calls. + * + * @return the singleton WebViewDatabase instance + */ + WebViewDatabase getWebViewDatabase(Context context); } |