Back to index

lightning-sunbird  0.9+nobinonly
nsStorageFormHistory.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Joe Hewitt <hewitt@netscape.com> (Original Author)
00024  *   Brett Wilson <brettw@gmail.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsStorageFormHistory.h"
00041 
00042 #include "nsIServiceManager.h"
00043 #include "nsIObserverService.h"
00044 #include "nsICategoryManager.h"
00045 #include "nsIDirectoryService.h"
00046 #include "nsAppDirectoryServiceDefs.h"
00047 #include "nsCRT.h"
00048 #include "nsString.h"
00049 #include "nsUnicharUtils.h"
00050 #include "nsReadableUtils.h"
00051 #include "nsIContent.h"
00052 #include "nsIDOMNode.h"
00053 #include "nsIDOMHTMLFormElement.h"
00054 #include "nsIDOMHTMLInputElement.h"
00055 #include "nsIDOMHTMLCollection.h"
00056 #include "nsIPrefService.h"
00057 #include "nsIPrefBranch.h"
00058 #include "nsIPrefBranch2.h"
00059 #include "nsVoidArray.h"
00060 #include "nsCOMArray.h"
00061 #include "mozStorageHelper.h"
00062 #include "mozStorageCID.h"
00063 #include "nsIAutoCompleteSimpleResult.h"
00064 #include "nsTArray.h"
00065 
00066 // The size of the database cache. This is the number of database PAGES that
00067 // can be cached in memory. Normally, pages are 1K unless the size has been
00068 // explicitly changed.
00069 //
00070 // 4MB should be much larger than normal form histories. Normal form histories
00071 // will be several hundered KB at most. If the form history is smaller, the
00072 // extra memory will never be allocated, so there is no penalty for larger
00073 // numbers. See StartCache
00074 #define DATABASE_CACHE_PAGES 4000
00075 
00076 // nsFormHistoryResult is a specialized autocomplete result class that knows
00077 // how to remove entries from the form history table.
00078 class nsFormHistoryResult : public nsIAutoCompleteSimpleResult
00079 {
00080 public:
00081   nsFormHistoryResult(const nsAString &aFieldName)
00082     : mFieldName(aFieldName) {}
00083 
00084   nsresult Init();
00085 
00086   NS_DECL_ISUPPORTS
00087 
00088   // Forward everything except RemoveValueAt to the internal result
00089   NS_IMETHOD GetSearchString(nsAString &_result)
00090   { return mResult->GetSearchString(_result); }
00091   NS_IMETHOD GetSearchResult(PRUint16 *_result)
00092   { return mResult->GetSearchResult(_result); }
00093   NS_IMETHOD GetDefaultIndex(PRInt32 *_result)
00094   { return mResult->GetDefaultIndex(_result); }
00095   NS_IMETHOD GetErrorDescription(nsAString &_result)
00096   { return mResult->GetErrorDescription(_result); }
00097   NS_IMETHOD GetMatchCount(PRUint32 *_result)
00098   { return mResult->GetMatchCount(_result); }
00099   NS_IMETHOD GetValueAt(PRInt32 aIndex, nsAString &_result)
00100   { return mResult->GetValueAt(aIndex, _result); }
00101   NS_IMETHOD GetCommentAt(PRInt32 aIndex, nsAString &_result)
00102   { return mResult->GetCommentAt(aIndex, _result); }
00103   NS_IMETHOD GetStyleAt(PRInt32 aIndex, nsAString &_result)
00104   { return mResult->GetStyleAt(aIndex, _result); }
00105   NS_IMETHOD RemoveValueAt(PRInt32 aRowIndex, PRBool aRemoveFromDB);
00106   NS_FORWARD_NSIAUTOCOMPLETESIMPLERESULT(mResult->)
00107 
00108 protected:
00109   nsCOMPtr<nsIAutoCompleteSimpleResult> mResult;
00110   nsString mFieldName;
00111 };
00112 
00113 NS_IMPL_ISUPPORTS2(nsFormHistoryResult,
00114                    nsIAutoCompleteResult, nsIAutoCompleteSimpleResult)
00115 
00116 nsresult
00117 nsFormHistoryResult::Init()
00118 {
00119   nsresult rv;
00120   mResult = do_CreateInstance(NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &rv);
00121   return rv;
00122 }
00123 
00124 NS_IMETHODIMP
00125 nsFormHistoryResult::RemoveValueAt(PRInt32 aRowIndex, PRBool aRemoveFromDB)
00126 {
00127   if (!aRemoveFromDB) {
00128     return mResult->RemoveValueAt(aRowIndex, aRemoveFromDB);
00129   }
00130 
00131   nsAutoString value;
00132   nsresult rv = mResult->GetValueAt(aRowIndex, value);
00133   NS_ENSURE_SUCCESS(rv, rv);
00134 
00135   rv = mResult->RemoveValueAt(aRowIndex, aRemoveFromDB);
00136   NS_ENSURE_SUCCESS(rv, rv);
00137 
00138   nsFormHistory* fh = nsFormHistory::GetInstance();
00139   NS_ENSURE_TRUE(fh, NS_ERROR_OUT_OF_MEMORY);
00140   return fh->RemoveEntry(mFieldName, value);
00141 }
00142 
00143 #define PREF_FORMFILL_BRANCH "browser.formfill."
00144 #define PREF_FORMFILL_ENABLE "enable"
00145 
00146 NS_INTERFACE_MAP_BEGIN(nsFormHistory)
00147   NS_INTERFACE_MAP_ENTRY(nsIFormHistory2)
00148   NS_INTERFACE_MAP_ENTRY(nsIFormHistoryPrivate)
00149   NS_INTERFACE_MAP_ENTRY(nsIObserver)
00150   NS_INTERFACE_MAP_ENTRY(nsIFormSubmitObserver)
00151   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00152   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
00153 NS_INTERFACE_MAP_END
00154 
00155 NS_IMPL_ADDREF(nsFormHistory)
00156 NS_IMPL_RELEASE(nsFormHistory)
00157 
00158 PRBool nsFormHistory::gFormHistoryEnabled = PR_FALSE;
00159 PRBool nsFormHistory::gPrefsInitialized = PR_FALSE;
00160 nsFormHistory* nsFormHistory::gFormHistory = nsnull;
00161 
00162 nsFormHistory::nsFormHistory()
00163 {
00164   NS_ASSERTION(!gFormHistory, "nsFormHistory must be used as a service");
00165   gFormHistory = this;
00166 }
00167 
00168 nsFormHistory::~nsFormHistory()
00169 {
00170   NS_ASSERTION(gFormHistory == this,
00171                "nsFormHistory must be used as a service");
00172   gFormHistory = nsnull;
00173 }
00174 
00175 nsresult
00176 nsFormHistory::Init()
00177 {
00178   nsresult rv = OpenDatabase();
00179   NS_ENSURE_SUCCESS(rv, rv);
00180 
00181   nsCOMPtr<nsIObserverService> service = do_GetService("@mozilla.org/observer-service;1");
00182   if (service)
00183     service->AddObserver(this, NS_EARLYFORMSUBMIT_SUBJECT, PR_TRUE);
00184 
00185   return NS_OK;
00186 }
00187 
00188 /* static */ PRBool
00189 nsFormHistory::FormHistoryEnabled()
00190 {
00191   if (!gPrefsInitialized) {
00192     nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
00193 
00194     prefService->GetBranch(PREF_FORMFILL_BRANCH,
00195                            getter_AddRefs(gFormHistory->mPrefBranch));
00196     gFormHistory->mPrefBranch->GetBoolPref(PREF_FORMFILL_ENABLE,
00197                                            &gFormHistoryEnabled);
00198 
00199     nsCOMPtr<nsIPrefBranch2> branchInternal =
00200       do_QueryInterface(gFormHistory->mPrefBranch);
00201     branchInternal->AddObserver(PREF_FORMFILL_ENABLE, gFormHistory, PR_TRUE);
00202 
00203     gPrefsInitialized = PR_TRUE;
00204   }
00205 
00206   return gFormHistoryEnabled;
00207 }
00208 
00209 
00212 
00213 NS_IMETHODIMP
00214 nsFormHistory::GetHasEntries(PRBool *aHasEntries)
00215 {
00216   mozStorageStatementScoper scope(mDBSelectEntries);
00217 
00218   PRBool hasMore;
00219   *aHasEntries = NS_SUCCEEDED(mDBSelectEntries->ExecuteStep(&hasMore)) && hasMore;
00220   return NS_OK;
00221 }
00222 
00223 NS_IMETHODIMP
00224 nsFormHistory::AddEntry(const nsAString &aName, const nsAString &aValue)
00225 {
00226   if (!FormHistoryEnabled())
00227     return NS_OK;
00228 
00229   mozStorageTransaction transaction(mDBConn, PR_FALSE);
00230 
00231   PRBool exists = PR_TRUE;
00232   EntryExists(aName, aValue, &exists);
00233   if (!exists) {
00234     mozStorageStatementScoper scope(mDBInsertNameValue);
00235     nsresult rv = mDBInsertNameValue->BindStringParameter(0, aName);
00236     NS_ENSURE_SUCCESS(rv, rv);
00237 
00238     rv = mDBInsertNameValue->BindStringParameter(1, aValue);
00239     NS_ENSURE_SUCCESS(rv, rv);
00240 
00241     rv = mDBInsertNameValue->Execute();
00242     NS_ENSURE_SUCCESS(rv, rv);
00243   }
00244   return transaction.Commit();
00245 }
00246 
00247 NS_IMETHODIMP
00248 nsFormHistory::EntryExists(const nsAString &aName, 
00249                            const nsAString &aValue, PRBool *_retval)
00250 {
00251   mozStorageStatementScoper scope(mDBFindEntry);
00252 
00253   nsresult rv = mDBFindEntry->BindStringParameter(0, aName);
00254   NS_ENSURE_SUCCESS(rv, rv);
00255 
00256   rv = mDBFindEntry->BindStringParameter(1, aValue);
00257   NS_ENSURE_SUCCESS(rv, rv);
00258 
00259   PRBool hasMore;
00260   *_retval = NS_SUCCEEDED(mDBFindEntry->ExecuteStep(&hasMore)) && hasMore;
00261   return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP
00265 nsFormHistory::NameExists(const nsAString &aName, PRBool *_retval)
00266 {
00267   mozStorageStatementScoper scope(mDBFindEntryByName);
00268 
00269   nsresult rv = mDBFindEntryByName->BindStringParameter(0, aName);
00270   NS_ENSURE_SUCCESS(rv, rv);
00271 
00272   PRBool hasMore;
00273   *_retval = (NS_SUCCEEDED(mDBFindEntryByName->ExecuteStep(&hasMore)) &&
00274               hasMore);
00275   return NS_OK;
00276 }
00277 
00278 NS_IMETHODIMP
00279 nsFormHistory::RemoveEntry(const nsAString &aName, const nsAString &aValue)
00280 {
00281   nsCOMPtr<mozIStorageStatement> dbDelete;
00282   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_formhistory WHERE fieldname=?1 AND value=?2"),
00283                                          getter_AddRefs(dbDelete));
00284   NS_ENSURE_SUCCESS(rv,rv);
00285 
00286   rv = dbDelete->BindStringParameter(0, aName);
00287   NS_ENSURE_SUCCESS(rv,rv);
00288 
00289   rv = dbDelete->BindStringParameter(1, aValue);
00290   NS_ENSURE_SUCCESS(rv, rv);
00291 
00292   return dbDelete->Execute();
00293 }
00294 
00295 NS_IMETHODIMP
00296 nsFormHistory::RemoveEntriesForName(const nsAString &aName)
00297 {
00298   nsCOMPtr<mozIStorageStatement> dbDelete;
00299   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_formhistory WHERE fieldname=?1"),
00300                                          getter_AddRefs(dbDelete));
00301   NS_ENSURE_SUCCESS(rv,rv);
00302 
00303   rv = dbDelete->BindStringParameter(0, aName);
00304   NS_ENSURE_SUCCESS(rv,rv);
00305 
00306   return dbDelete->Execute();
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsFormHistory::RemoveAllEntries()
00311 {
00312   nsCOMPtr<mozIStorageStatement> dbDeleteAll;
00313   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_formhistory"),
00314                                          getter_AddRefs(dbDeleteAll));
00315   NS_ENSURE_SUCCESS(rv,rv);
00316 
00317   return dbDeleteAll->Execute();
00318 }
00319 
00322 
00323 NS_IMETHODIMP
00324 nsFormHistory::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) 
00325 {
00326   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00327     mPrefBranch->GetBoolPref(PREF_FORMFILL_ENABLE, &gFormHistoryEnabled);
00328   }
00329 
00330   return NS_OK;
00331 }
00332 
00335 
00336 NS_IMETHODIMP
00337 nsFormHistory::Notify(nsIContent* aFormNode, nsIDOMWindowInternal* aWindow, nsIURI* aActionURL, PRBool* aCancelSubmit)
00338 {
00339   if (!FormHistoryEnabled())
00340     return NS_OK;
00341 
00342   nsCOMPtr<nsIDOMHTMLFormElement> formElt = do_QueryInterface(aFormNode);
00343   NS_ENSURE_TRUE(formElt, NS_ERROR_FAILURE);
00344 
00345   nsCOMPtr<nsIDOMHTMLCollection> elts;
00346   formElt->GetElements(getter_AddRefs(elts));
00347   
00348   const char *textString = "text";
00349   
00350   PRUint32 length;
00351   elts->GetLength(&length);
00352   for (PRUint32 i = 0; i < length; ++i) {
00353     nsCOMPtr<nsIDOMNode> node;
00354     elts->Item(i, getter_AddRefs(node));
00355     nsCOMPtr<nsIDOMHTMLInputElement> inputElt = do_QueryInterface(node);
00356     if (inputElt) {
00357       // Filter only inputs that are of type "text"
00358       nsAutoString type;
00359       inputElt->GetType(type);
00360       if (type.EqualsIgnoreCase(textString)) {
00361         // If this input has a name/id and value, add it to the database
00362         nsAutoString value;
00363         inputElt->GetValue(value);
00364         if (!value.IsEmpty()) {
00365           nsAutoString name;
00366           inputElt->GetName(name);
00367           if (name.IsEmpty())
00368             inputElt->GetId(name);
00369           
00370           if (!name.IsEmpty())
00371             AddEntry(name, value);
00372         }
00373       }
00374     }
00375   }
00376 
00377   return NS_OK;
00378 }
00379 nsresult
00380 nsFormHistory::OpenDatabase()
00381 {
00382   // init DB service and obtain a connection
00383   nsresult rv;
00384   mStorageService = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
00385   NS_ENSURE_SUCCESS(rv, rv);
00386 
00387   nsCOMPtr<nsIFile> formHistoryFile;
00388   rv = GetDatabaseFile(getter_AddRefs(formHistoryFile));
00389   NS_ENSURE_SUCCESS(rv, rv);
00390   rv = mStorageService->OpenDatabase(formHistoryFile, getter_AddRefs(mDBConn));
00391   NS_ENSURE_SUCCESS(rv, rv);
00392 
00393   // We execute many statements before the database cache is started to create
00394   // the tables (which can not be done which the cache is locked in memory by
00395   // the dummy statement--see StartCache). This transaction will keep the cache
00396   // between these statements, which should improve startup performance because
00397   // we won't have to keep requesting pages from the OS.
00398   mozStorageTransaction transaction(mDBConn, PR_FALSE);
00399 
00400   PRBool exists;
00401   mDBConn->TableExists(NS_LITERAL_CSTRING("moz_formhistory"), &exists);
00402   if (!exists) {
00403     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_formhistory (id INTEGER PRIMARY KEY, fieldname LONGVARCHAR, value LONGVARCHAR)"));
00404     NS_ENSURE_SUCCESS(rv, rv);
00405     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_formhistory_index ON moz_formhistory (fieldname)"));
00406     NS_ENSURE_SUCCESS(rv, rv);
00407   }
00408 
00409   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM moz_formhistory"),
00410                                 getter_AddRefs(mDBSelectEntries));
00411   NS_ENSURE_SUCCESS(rv, rv);
00412 
00413   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM moz_formhistory WHERE fieldname=?1 AND value=?2"),
00414                                 getter_AddRefs(mDBFindEntry));
00415   NS_ENSURE_SUCCESS(rv, rv);
00416 
00417   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM moz_formhistory WHERE fieldname=?1"),
00418                                 getter_AddRefs(mDBFindEntryByName));
00419   NS_ENSURE_SUCCESS(rv,rv);
00420 
00421   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT value FROM moz_formhistory WHERE fieldname=?1"),
00422                                 getter_AddRefs(mDBGetMatchingField));
00423   NS_ENSURE_SUCCESS(rv,rv);
00424 
00425   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_formhistory (fieldname, value) VALUES (?1, ?2)"),
00426                                 getter_AddRefs(mDBInsertNameValue));
00427   NS_ENSURE_SUCCESS(rv, rv);
00428 
00429   // should commit before starting cache
00430   transaction.Commit();
00431 
00432   // ignore errors since the cache is not critical for operation
00433   StartCache();
00434 
00435 #ifdef MOZ_MORKREADER
00436   if (!exists) {
00437     // Locate the old formhistory.dat file and import it.
00438     nsCOMPtr<nsIFile> historyFile;
00439     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
00440                                 getter_AddRefs(historyFile));
00441     if (NS_SUCCEEDED(rv)) {
00442       historyFile->Append(NS_LITERAL_STRING("formhistory.dat"));
00443 
00444       nsCOMPtr<nsIFormHistoryImporter> importer = new nsFormHistoryImporter();
00445       NS_ENSURE_TRUE(importer, NS_ERROR_OUT_OF_MEMORY);
00446       importer->ImportFormHistory(historyFile, this);
00447     }
00448   }
00449 #endif
00450 
00451   return NS_OK;
00452 }
00453 
00454 
00455 nsresult
00456 nsFormHistory::GetDatabaseFile(nsIFile** aFile)
00457 {
00458   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aFile);
00459   NS_ENSURE_SUCCESS(rv, rv);
00460   return (*aFile)->Append(NS_LITERAL_STRING("formhistory.sqlite"));
00461 }
00462 
00463 
00464 // nsFormHistory::StartCache
00465 //
00466 //    This function starts the dummy statement that locks the cache in memory.
00467 //    As long as there is an open connection sharing the same cache, the cache
00468 //    won't be expired. Therefore, we create a dummy table with some data in
00469 //    it, and open a statement over the data. As long as this statement is
00470 //    open, we can go fast.
00471 //
00472 //    This dummy statement prevents the schema from being modified. If you
00473 //    want to add or change a table or index schema, you must stop the dummy
00474 //    statement first. See nsNavHistory::StartDummyStatement for a slightly
00475 //    more detailed discussion.
00476 //
00477 //    Note that we should not use a transaction in this function since that
00478 //    will commit the dummy statement and everything will break.
00479 //
00480 //    This function also initializes the cache.
00481 
00482 nsresult
00483 nsFormHistory::StartCache()
00484 {
00485   // do nothing if the dummy statement is already running
00486   if (mDummyStatement)
00487     return NS_OK;
00488 
00489   // dummy database connection
00490   nsCOMPtr<nsIFile> formHistoryFile;
00491   nsresult rv = GetDatabaseFile(getter_AddRefs(formHistoryFile));
00492   NS_ENSURE_SUCCESS(rv, rv);
00493   rv = mStorageService->OpenDatabase(formHistoryFile,
00494                                      getter_AddRefs(mDummyConnection));
00495   NS_ENSURE_SUCCESS(rv, rv);
00496 
00497   // Make sure the dummy table exists
00498   PRBool tableExists;
00499   rv = mDummyConnection->TableExists(NS_LITERAL_CSTRING("moz_dummy_table"), &tableExists);
00500   NS_ENSURE_SUCCESS(rv, rv);
00501   if (! tableExists) {
00502     rv = mDummyConnection->ExecuteSimpleSQL(
00503         NS_LITERAL_CSTRING("CREATE TABLE moz_dummy_table (id INTEGER PRIMARY KEY)"));
00504     NS_ENSURE_SUCCESS(rv, rv);
00505   }
00506 
00507   // This table is guaranteed to have something in it and will keep the dummy
00508   // statement open. If the table is empty, it won't hold the statement open.
00509   // the PRIMARY KEY value on ID means that it is unique. The OR IGNORE means
00510   // that if there is already a value of 1 there, this insert will be ignored,
00511   // which is what we want so as to avoid growing the table infinitely.
00512   rv = mDummyConnection->ExecuteSimpleSQL(
00513       NS_LITERAL_CSTRING("INSERT OR IGNORE INTO moz_dummy_table VALUES (1)"));
00514   NS_ENSURE_SUCCESS(rv, rv);
00515 
00516   rv = mDummyConnection->CreateStatement(NS_LITERAL_CSTRING(
00517       "SELECT id FROM moz_dummy_table LIMIT 1"),
00518     getter_AddRefs(mDummyStatement));
00519   NS_ENSURE_SUCCESS(rv, rv);
00520 
00521   // we have to step the dummy statement so that it will hold a lock on the DB
00522   PRBool dummyHasResults;
00523   rv = mDummyStatement->ExecuteStep(&dummyHasResults);
00524   NS_ENSURE_SUCCESS(rv, rv);
00525 
00526   // Set the cache size
00527   nsCAutoString cacheSizePragma("PRAGMA cache_size=");
00528   cacheSizePragma.AppendInt(DATABASE_CACHE_PAGES);
00529   rv = mDummyConnection->ExecuteSimpleSQL(cacheSizePragma);
00530   NS_ENSURE_SUCCESS(rv, rv);
00531 
00532   // preload the cache
00533   rv = mDummyConnection->Preload();
00534   NS_ENSURE_SUCCESS(rv, rv);
00535 
00536   return NS_OK;
00537 }
00538 
00539 
00540 // nsFormHistory::StopCache
00541 //
00542 //    Call this before doing any schema modifying operations. You should
00543 //    start the dummy statement again to give good performance.
00544 //    See StartCache.
00545 
00546 nsresult
00547 nsFormHistory::StopCache()
00548 {
00549   // do nothing if the dummy statement isn't running
00550   if (! mDummyStatement)
00551     return NS_OK;
00552 
00553   nsresult rv = mDummyStatement->Reset();
00554   NS_ENSURE_SUCCESS(rv, rv);
00555 
00556   mDummyStatement = nsnull;
00557   return NS_OK;
00558 }
00559 
00560 
00561 nsresult
00562 nsFormHistory::AutoCompleteSearch(const nsAString &aInputName,
00563                                   const nsAString &aInputValue,
00564                                   nsIAutoCompleteSimpleResult *aPrevResult,
00565                                   nsIAutoCompleteResult **aResult)
00566 {
00567   if (!FormHistoryEnabled())
00568     return NS_OK;
00569 
00570   nsCOMPtr<nsIAutoCompleteSimpleResult> result;
00571 
00572   if (aPrevResult) {
00573     result = aPrevResult;
00574 
00575     PRUint32 matchCount;
00576     result->GetMatchCount(&matchCount);
00577 
00578     for (PRInt32 i = matchCount - 1; i >= 0; --i) {
00579       nsAutoString match;
00580       result->GetValueAt(i, match);
00581       if (!StringBeginsWith(match, aInputValue,
00582                             nsCaseInsensitiveStringComparator())) {
00583         result->RemoveValueAt(i, PR_FALSE);
00584       }
00585     }
00586   } else {
00587     nsCOMPtr<nsFormHistoryResult> fhResult =
00588       new nsFormHistoryResult(aInputName);
00589     NS_ENSURE_TRUE(fhResult, NS_ERROR_OUT_OF_MEMORY);
00590     nsresult rv = fhResult->Init();
00591     NS_ENSURE_SUCCESS(rv, rv);
00592     NS_REINTERPRET_CAST(nsCOMPtr<nsIAutoCompleteSimpleResult>*, &fhResult)->swap(result);
00593 
00594     result->SetSearchString(aInputValue);
00595 
00596     // generates query string             
00597     mozStorageStatementScoper scope(mDBGetMatchingField);
00598     rv = mDBGetMatchingField->BindStringParameter(0, aInputName);
00599     NS_ENSURE_SUCCESS(rv,rv);
00600 
00601     PRBool hasMore = PR_FALSE;
00602     PRUint32 count = 0;
00603     while (NS_SUCCEEDED(mDBGetMatchingField->ExecuteStep(&hasMore)) &&
00604            hasMore) {
00605       nsAutoString entryString;
00606       mDBGetMatchingField->GetString(0, entryString);
00607       // filters out irrelevant results
00608       if(StringBeginsWith(entryString, aInputValue,
00609                           nsCaseInsensitiveStringComparator())) {
00610         result->AppendMatch(entryString, EmptyString());
00611         ++count;
00612       }
00613     }
00614     if (count > 0) {
00615       result->SetSearchResult(nsIAutoCompleteResult::RESULT_SUCCESS);
00616       result->SetDefaultIndex(0);
00617     } else {
00618       result->SetSearchResult(nsIAutoCompleteResult::RESULT_NOMATCH);
00619       result->SetDefaultIndex(-1);
00620     }
00621   }
00622 
00623   *aResult = result;
00624   NS_IF_ADDREF(*aResult);
00625   return NS_OK;
00626 }
00627 
00628 #ifdef MOZ_MORKREADER
00629 
00630 // Columns for form history rows
00631 enum {
00632   kNameColumn,
00633   kValueColumn,
00634   kColumnCount // keep me last
00635 };
00636 
00637 static const char * const gColumnNames[] = {
00638   "Name", "Value"
00639 };
00640 
00641 struct FormHistoryImportClosure
00642 {
00643   FormHistoryImportClosure(nsMorkReader *aReader, nsIFormHistory2 *aFormHistory)
00644     : reader(aReader), formHistory(aFormHistory), byteOrderColumn(-1),
00645       swapBytes(PR_FALSE)
00646   {
00647     for (PRUint32 i = 0; i < kColumnCount; ++i) {
00648       columnIndexes[i] = -1;
00649     }
00650   }
00651 
00652   // Back pointers to the reader and history we're operating on
00653   const nsMorkReader *reader;
00654   nsIFormHistory2 *formHistory;
00655 
00656   // Indexes of the columns that we care about
00657   PRInt32 columnIndexes[kColumnCount];
00658   PRInt32 byteOrderColumn;
00659 
00660   // Whether we need to swap bytes (file format is other-endian)
00661   PRPackedBool swapBytes;
00662 };
00663 
00664 // Reverses the high and low bytes in a PRUnichar buffer.
00665 // This is used if the file format has a different endianness from the
00666 // current architecture.
00667 static void SwapBytes(PRUnichar* aBuffer)
00668 {
00669   for (PRUnichar *b = aBuffer; *b; b++)
00670   {
00671     PRUnichar c = *b;
00672     *b = (0xff & (c >> 8)) | (c << 8);
00673   }
00674 }
00675 
00676 // Enumerator callback to add an entry to the FormHistory
00677 /* static */ PLDHashOperator PR_CALLBACK
00678 nsFormHistoryImporter::AddToFormHistoryCB(const nsCSubstring &aRowID,
00679                                           const nsTArray<nsCString> *aValues,
00680                                           void *aData)
00681 {
00682   FormHistoryImportClosure *data = NS_STATIC_CAST(FormHistoryImportClosure*,
00683                                                   aData);
00684   const nsMorkReader *reader = data->reader;
00685   nsCString values[kColumnCount];
00686   const PRUnichar* valueStrings[kColumnCount];
00687   PRUint32 valueLengths[kColumnCount];
00688   const PRInt32 *columnIndexes = data->columnIndexes;
00689   PRInt32 i;
00690 
00691   // Values are in UTF16.
00692 
00693   for (i = 0; i < kColumnCount; ++i) {
00694     if (columnIndexes[i] == -1) {
00695       // We didn't find this column in the map
00696       continue;
00697     }
00698 
00699     values[i] = (*aValues)[columnIndexes[i]];
00700     reader->NormalizeValue(values[i]);
00701 
00702     PRUint32 length;
00703     const char *bytes;
00704     if (values[i].IsEmpty()) {
00705       bytes = "\0";
00706       length = 0;
00707     } else {
00708       length = values[i].Length() / 2;
00709 
00710       // add an extra null byte onto the end, so that the buffer ends
00711       // with a complete unicode null character.
00712       values[i].Append('\0');
00713 
00714       // Swap the bytes in the unicode characters if necessary.
00715       if (data->swapBytes) {
00716         SwapBytes(NS_REINTERPRET_CAST(PRUnichar*, values[i].BeginWriting()));
00717       }
00718       bytes = values[i].get();
00719     }
00720     valueStrings[i] = NS_REINTERPRET_CAST(const PRUnichar*, bytes);
00721     valueLengths[i] = length;
00722   }
00723 
00724   data->formHistory->AddEntry(nsDependentString(valueStrings[kNameColumn],
00725                                                 valueLengths[kNameColumn]),
00726                               nsDependentString(valueStrings[kValueColumn],
00727                                                 valueLengths[kValueColumn]));
00728   return PL_DHASH_NEXT;
00729 }
00730 
00731 NS_IMPL_ISUPPORTS1(nsFormHistoryImporter, nsIFormHistoryImporter)
00732 
00733 NS_IMETHODIMP
00734 nsFormHistoryImporter::ImportFormHistory(nsIFile *aFile,
00735                                          nsIFormHistory2 *aFormHistory)
00736 {
00737   // Check that the file exists before we try to open it
00738   PRBool exists;
00739   aFile->Exists(&exists);
00740   if (!exists) {
00741     return NS_OK;
00742   }
00743   
00744   nsMorkReader reader;
00745   nsresult rv = reader.Init();
00746   NS_ENSURE_SUCCESS(rv, rv);
00747 
00748   rv = reader.Read(aFile);
00749   NS_ENSURE_SUCCESS(rv, rv);
00750 
00751   // Gather up the column ids so we don't need to find them on each row
00752   FormHistoryImportClosure data(&reader, aFormHistory);
00753   const nsTArray<nsMorkReader::MorkColumn> columns = reader.GetColumns();
00754   for (PRUint32 i = 0; i < columns.Length(); ++i) {
00755     const nsCSubstring &name = columns[i].name;
00756     for (PRUint32 j = 0; j < kColumnCount; ++j) {
00757       if (name.Equals(gColumnNames[j])) {
00758         data.columnIndexes[j] = i;
00759         break;
00760       }
00761     }
00762     if (name.EqualsLiteral("ByteOrder")) {
00763       data.byteOrderColumn = i;
00764     }
00765   }
00766 
00767   // Determine the byte order from the table's meta-row.
00768   const nsTArray<nsCString> *metaRow = reader.GetMetaRow();
00769   if (metaRow && data.byteOrderColumn != -1) {
00770     const nsCString &byteOrder = (*metaRow)[data.byteOrderColumn];
00771     // Note whether the file uses a non-native byte ordering.
00772     // If it does, we'll have to swap bytes for PRUnichar values.
00773     // "BBBB" and "llll" are the only recognized values, anything
00774     // else is garbage and the file will be treated as native-endian
00775     // (no swapping).
00776     nsCAutoString byteOrderValue(byteOrder);
00777     reader.NormalizeValue(byteOrderValue);
00778 #ifdef IS_LITTLE_ENDIAN
00779     data.swapBytes = byteOrderValue.EqualsLiteral("BBBB");
00780 #else
00781     data.swapBytes = byteOrderValue.EqualsLiteral("llll");
00782 #endif
00783   }
00784 #if defined(XP_MACOSX) && defined(IS_LITTLE_ENDIAN)
00785   // The meta row and its ByteOrder field was introduced in 1.8.0.2.
00786   // If it's not present, treat the formhistory db as using native byte
00787   // ordering (as was done prior to 1.8.0.2).
00788   // Exception: the ByteOrder field was always present since the initial
00789   // x86 Mac release, so if we're on one of those, and the file doesn't
00790   // have a ByteOrder field, it most likely came from a ppc Mac and needs
00791   // its bytes swapped.  nsFormHistory in 1.8.0.2 swapped the bytes, this
00792   // importer should behave the same way.
00793   else {
00794     data.swapBytes = PR_TRUE;
00795   }
00796 #endif
00797 
00798   // Add the rows to form history
00799   nsCOMPtr<nsIFormHistoryPrivate> fhPrivate = do_QueryInterface(aFormHistory);
00800   NS_ENSURE_TRUE(fhPrivate, NS_ERROR_FAILURE);
00801 
00802   mozIStorageConnection *conn = fhPrivate->GetStorageConnection();
00803   NS_ENSURE_TRUE(conn, NS_ERROR_NOT_INITIALIZED);
00804   mozStorageTransaction transaction(conn, PR_FALSE);
00805 
00806   reader.EnumerateRows(AddToFormHistoryCB, &data);
00807   return transaction.Commit();
00808 }
00809 #endif