summaryrefslogtreecommitdiffstats
path: root/chrome/browser/webdata
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/webdata
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/webdata')
-rw-r--r--chrome/browser/webdata/web_data_service.cc687
-rw-r--r--chrome/browser/webdata/web_data_service.h508
-rw-r--r--chrome/browser/webdata/web_database.cc873
-rw-r--r--chrome/browser/webdata/web_database.h167
-rw-r--r--chrome/browser/webdata/web_database_unittest.cc587
5 files changed, 2822 insertions, 0 deletions
diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc
new file mode 100644
index 0000000..350e80c
--- /dev/null
+++ b/chrome/browser/webdata/web_data_service.cc
@@ -0,0 +1,687 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "chrome/browser/webdata/web_data_service.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/ie7_password.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/common/chrome_constants.h"
+#include "webkit/glue/password_form.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataService implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+WebDataService::WebDataService() : should_commit_(false),
+ next_request_handle_(1),
+ thread_(NULL),
+ db_(NULL) {
+}
+
+WebDataService::~WebDataService() {
+ if (thread_) {
+ Shutdown();
+ }
+}
+
+bool WebDataService::Init(const std::wstring& profile_path) {
+ std::wstring path = profile_path;
+ file_util::AppendToPath(&path, chrome::kWebDataFilename);
+ return InitWithPath(path);
+}
+
+bool WebDataService::InitWithPath(const std::wstring& path) {
+ thread_ = new Thread("Chrome_WebDataThread");
+
+ if (!thread_->Start()) {
+ delete thread_;
+ thread_ = NULL;
+ return false;
+ }
+
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::InitializeDatabase,
+ path));
+ return true;
+}
+
+class ShutdownTask : public Task {
+ public:
+ explicit ShutdownTask(WebDataService* wds) : service_(wds) {
+ }
+ virtual void Run() {
+ service_->ShutdownDatabase();
+ }
+
+ private:
+
+ WebDataService* service_;
+};
+
+void WebDataService::Shutdown() {
+ if (thread_) {
+ // We cannot use NewRunnableMethod() because this can be called from our
+ // destructor. NewRunnableMethod() would AddRef() this instance.
+ ScheduleTask(new ShutdownTask(this));
+
+ // The thread destructor sends a message to terminate the thread and waits
+ // until the thread has exited.
+ delete thread_;
+ thread_ = NULL;
+ }
+}
+
+bool WebDataService::IsRunning() {
+ return thread_ != NULL;
+}
+
+void WebDataService::ScheduleCommit() {
+ if (should_commit_ == false) {
+ should_commit_ = true;
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::Commit));
+ }
+}
+
+void WebDataService::ScheduleTask(Task* t) {
+ if (thread_)
+ thread_->message_loop()->PostTask(FROM_HERE, t);
+ else
+ NOTREACHED() << "Task scheduled after Shutdown()";
+}
+
+void WebDataService::RegisterRequest(WebDataRequest* request) {
+ AutoLock l(pending_lock_);
+ pending_requests_[request->GetHandle()] = request;
+}
+
+void WebDataService::CancelRequest(Handle h) {
+ AutoLock l(pending_lock_);
+ RequestMap::iterator i = pending_requests_.find(h);
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Canceling a nonexistant web data service request";
+ return;
+ }
+ i->second->Cancel();
+}
+
+void WebDataService::RequestCompleted(Handle h) {
+ pending_lock_.Acquire();
+ RequestMap::iterator i = pending_requests_.find(h);
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Request completed called for an unknown request";
+ pending_lock_.Release();
+ return;
+ }
+
+ // Take ownership of the request object and remove it from the map.
+ scoped_ptr<WebDataRequest> request(i->second);
+ pending_requests_.erase(i);
+ pending_lock_.Release();
+
+ // Notify the consumer if needed.
+ WebDataServiceConsumer* consumer;
+ if (!request->IsCancelled() && (consumer = request->GetConsumer())) {
+ consumer->OnWebDataServiceRequestDone(request->GetHandle(),
+ request->GetResult());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Keywords.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::AddKeyword(const TemplateURL& url) {
+ GenericRequest<TemplateURL>* request =
+ new GenericRequest<TemplateURL>(this, GetNextRequestHandle(), NULL, url);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::AddKeywordImpl,
+ request));
+}
+
+void WebDataService::RemoveKeyword(const TemplateURL& url) {
+ GenericRequest<TemplateURL::IDType>* request =
+ new GenericRequest<TemplateURL::IDType>(this, GetNextRequestHandle(),
+ NULL, url.id());
+ RegisterRequest(request);
+ ScheduleTask(
+ NewRunnableMethod(this, &WebDataService::RemoveKeywordImpl, request));
+}
+
+void WebDataService::UpdateKeyword(const TemplateURL& url) {
+ GenericRequest<TemplateURL>* request =
+ new GenericRequest<TemplateURL>(this, GetNextRequestHandle(), NULL, url);
+ RegisterRequest(request);
+ ScheduleTask(
+ NewRunnableMethod(this, &WebDataService::UpdateKeywordImpl, request));
+}
+
+WebDataService::Handle WebDataService::GetKeywords(
+ WebDataServiceConsumer* consumer) {
+ WebDataRequest* request =
+ new WebDataRequest(this, GetNextRequestHandle(), consumer);
+ RegisterRequest(request);
+ ScheduleTask(
+ NewRunnableMethod(this,
+ &WebDataService::GetKeywordsImpl,
+ request));
+ return request->GetHandle();
+}
+
+void WebDataService::SetDefaultSearchProvider(const TemplateURL* url) {
+ GenericRequest<TemplateURL::IDType>* request =
+ new GenericRequest<TemplateURL::IDType>(this,
+ GetNextRequestHandle(),
+ NULL,
+ url ? url->id() : 0);
+ RegisterRequest(request);
+ ScheduleTask(
+ NewRunnableMethod(this, &WebDataService::SetDefaultSearchProviderImpl,
+ request));
+}
+
+void WebDataService::SetBuiltinKeywordVersion(int version) {
+ GenericRequest<int>* request =
+ new GenericRequest<int>(this, GetNextRequestHandle(), NULL, version);
+ RegisterRequest(request);
+ ScheduleTask(
+ NewRunnableMethod(this, &WebDataService::SetBuiltinKeywordVersionImpl,
+ request));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Web Apps
+//
+//////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::SetWebAppImage(const GURL& app_url,
+ const SkBitmap& image) {
+ GenericRequest2<GURL, SkBitmap>* request =
+ new GenericRequest2<GURL, SkBitmap>(this, GetNextRequestHandle(),
+ NULL, app_url, image);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::SetWebAppImageImpl,
+ request));
+}
+
+void WebDataService::SetWebAppHasAllImages(const GURL& app_url,
+ bool has_all_images) {
+ GenericRequest2<GURL, bool>* request =
+ new GenericRequest2<GURL, bool>(this, GetNextRequestHandle(),
+ NULL, app_url, has_all_images);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::SetWebAppHasAllImagesImpl,
+ request));
+}
+
+void WebDataService::RemoveWebApp(const GURL& app_url) {
+ GenericRequest<GURL>* request =
+ new GenericRequest<GURL>(this, GetNextRequestHandle(), NULL, app_url);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::RemoveWebAppImpl,
+ request));
+}
+
+WebDataService::Handle WebDataService::GetWebAppImages(
+ const GURL& app_url,
+ WebDataServiceConsumer* consumer) {
+ GenericRequest<GURL>* request =
+ new GenericRequest<GURL>(this, GetNextRequestHandle(), consumer, app_url);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::GetWebAppImagesImpl,
+ request));
+ return request->GetHandle();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Password manager.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::AddLogin(const PasswordForm& form) {
+ GenericRequest<PasswordForm>* request =
+ new GenericRequest<PasswordForm>(this, GetNextRequestHandle(), NULL,
+ form);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::AddLoginImpl,
+ request));
+}
+
+void WebDataService::AddIE7Login(const IE7PasswordInfo& info) {
+ GenericRequest<IE7PasswordInfo>* request =
+ new GenericRequest<IE7PasswordInfo>(this, GetNextRequestHandle(), NULL,
+ info);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::AddIE7LoginImpl,
+ request));
+}
+
+void WebDataService::UpdateLogin(const PasswordForm& form) {
+ GenericRequest<PasswordForm>* request =
+ new GenericRequest<PasswordForm>(this, GetNextRequestHandle(),
+ NULL, form);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::UpdateLoginImpl,
+ request));
+}
+
+void WebDataService::RemoveLogin(const PasswordForm& form) {
+ GenericRequest<PasswordForm>* request =
+ new GenericRequest<PasswordForm>(this, GetNextRequestHandle(), NULL,
+ form);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::RemoveLoginImpl,
+ request));
+}
+
+void WebDataService::RemoveIE7Login(const IE7PasswordInfo& info) {
+ GenericRequest<IE7PasswordInfo>* request =
+ new GenericRequest<IE7PasswordInfo>(this, GetNextRequestHandle(), NULL,
+ info);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::RemoveIE7LoginImpl,
+ request));
+}
+
+void WebDataService::RemoveLoginsCreatedBetween(const Time delete_begin,
+ const Time delete_end) {
+ GenericRequest2<Time, Time>* request =
+ new GenericRequest2<Time, Time>(this,
+ GetNextRequestHandle(),
+ NULL,
+ delete_begin,
+ delete_end);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::RemoveLoginsCreatedBetweenImpl, request));
+}
+
+void WebDataService::RemoveLoginsCreatedAfter(const Time delete_begin) {
+ RemoveLoginsCreatedBetween(delete_begin, Time());
+}
+
+WebDataService::Handle WebDataService::GetLogins(
+ const PasswordForm& form,
+ WebDataServiceConsumer* consumer) {
+ GenericRequest<PasswordForm>* request =
+ new GenericRequest<PasswordForm>(this, GetNextRequestHandle(),
+ consumer, form);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::GetLoginsImpl,
+ request));
+ return request->GetHandle();
+}
+
+WebDataService::Handle WebDataService::GetIE7Login(
+ const IE7PasswordInfo& info,
+ WebDataServiceConsumer* consumer) {
+ GenericRequest<IE7PasswordInfo>* request =
+ new GenericRequest<IE7PasswordInfo>(this, GetNextRequestHandle(),
+ consumer, info);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::GetIE7LoginImpl,
+ request));
+ return request->GetHandle();
+}
+
+WebDataService::Handle WebDataService::GetAllAutofillableLogins(
+ WebDataServiceConsumer* consumer) {
+ WebDataRequest* request =
+ new WebDataRequest(this, GetNextRequestHandle(), consumer);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::GetAllAutofillableLoginsImpl,
+ request));
+ return request->GetHandle();
+}
+
+WebDataService::Handle WebDataService::GetAllLogins(
+ WebDataServiceConsumer* consumer) {
+ WebDataRequest* request =
+ new WebDataRequest(this, GetNextRequestHandle(), consumer);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::GetAllLoginsImpl,
+ request));
+ return request->GetHandle();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The following methods are executed in Chrome_WebDataThread.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::Commit() {
+ if (should_commit_) {
+ should_commit_ = false;
+
+ if (db_) {
+ db_->CommitTransaction();
+ db_->BeginTransaction();
+ }
+ }
+}
+
+void WebDataService::InitializeDatabase(const std::wstring& path) {
+ DCHECK(!db_);
+ // In the rare case where the db fails to initialize a dialog may get shown
+ // the blocks the caller, yet allows other messages through. For this reason
+ // we only set db_ to the created database if creation is successful. That
+ // way other methods won't do anything as db_ is still NULL.
+ WebDatabase* db = new WebDatabase();
+ if (!db->Init(path)) {
+ NOTREACHED() << "Cannot initialize the web database";
+ delete db;
+ return;
+ }
+
+ db_ = db;
+
+ db_->BeginTransaction();
+}
+
+void WebDataService::ShutdownDatabase() {
+ if (db_) {
+ db_->CommitTransaction();
+ delete db_;
+ db_ = NULL;
+ }
+}
+
+//
+// Keywords.
+//
+void WebDataService::AddKeywordImpl(GenericRequest<TemplateURL>* request) {
+ if (db_ && !request->IsCancelled()) {
+ db_->AddKeyword(request->GetArgument());
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::RemoveKeywordImpl(
+ GenericRequest<TemplateURL::IDType>* request) {
+ if (db_ && !request->IsCancelled()) {
+ DCHECK(request->GetArgument());
+ db_->RemoveKeyword(request->GetArgument());
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::UpdateKeywordImpl(
+ GenericRequest<TemplateURL>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (!db_->UpdateKeyword(request->GetArgument()))
+ NOTREACHED();
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetKeywordsImpl(WebDataRequest* request) {
+ if (db_ && !request->IsCancelled()) {
+ WDKeywordsResult result;
+ db_->GetKeywords(&result.keywords);
+ result.default_search_provider_id = db_->GetDefaulSearchProviderID();
+ result.builtin_keyword_version = db_->GetBuitinKeywordVersion();
+ request->SetResult(
+ new WDResult<WDKeywordsResult>(KEYWORDS_RESULT, result));
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::SetDefaultSearchProviderImpl(
+ GenericRequest<TemplateURL::IDType>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (!db_->SetDefaultSearchProviderID(request->GetArgument()))
+ NOTREACHED();
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::SetBuiltinKeywordVersionImpl(
+ GenericRequest<int>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (!db_->SetBuitinKeywordVersion(request->GetArgument()))
+ NOTREACHED();
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+//
+// Password manager support.
+//
+void WebDataService::AddLoginImpl(GenericRequest<PasswordForm>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->AddLogin(request->GetArgument()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::AddIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->AddIE7Login(request->GetArgument()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::UpdateLoginImpl(GenericRequest<PasswordForm>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->UpdateLogin(request->GetArgument()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::RemoveLoginImpl(GenericRequest<PasswordForm>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->RemoveLogin(request->GetArgument()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::RemoveIE7LoginImpl(
+ GenericRequest<IE7PasswordInfo>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->RemoveIE7Login(request->GetArgument()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::RemoveLoginsCreatedBetweenImpl(
+ GenericRequest2<Time, Time>* request) {
+ if (db_ && !request->IsCancelled()) {
+ if (db_->RemoveLoginsCreatedBetween(request->GetArgument1(),
+ request->GetArgument2()))
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetLoginsImpl(GenericRequest<PasswordForm>* request) {
+ if (db_ && !request->IsCancelled()) {
+ std::vector<PasswordForm*> forms;
+ db_->GetLogins(request->GetArgument(), &forms);
+ request->SetResult(
+ new WDResult<std::vector<PasswordForm*> >(PASSWORD_RESULT, forms));
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetIE7LoginImpl(
+ GenericRequest<IE7PasswordInfo>* request) {
+ if (db_ && !request->IsCancelled()) {
+ IE7PasswordInfo result;
+ db_->GetIE7Login(request->GetArgument(), &result);
+ request->SetResult(
+ new WDResult<IE7PasswordInfo>(PASSWORD_IE7_RESULT, result));
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetAllAutofillableLoginsImpl(WebDataRequest* request) {
+ if (db_ && !request->IsCancelled()) {
+ std::vector<PasswordForm*> forms;
+ db_->GetAllLogins(&forms, false);
+ request->SetResult(
+ new WDResult<std::vector<PasswordForm*> >(PASSWORD_RESULT, forms));
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetAllLoginsImpl(WebDataRequest* request) {
+ if (db_ && !request->IsCancelled()) {
+ std::vector<PasswordForm*> forms;
+ db_->GetAllLogins(&forms, true);
+ request->SetResult(
+ new WDResult<std::vector<PasswordForm*> >(PASSWORD_RESULT, forms));
+ }
+ request->RequestComplete();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Web Apps implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::SetWebAppImageImpl(
+ GenericRequest2<GURL, SkBitmap>* request) {
+ if (db_ && !request->IsCancelled()) {
+ db_->SetWebAppImage(request->GetArgument1(), request->GetArgument2());
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::SetWebAppHasAllImagesImpl(
+ GenericRequest2<GURL, bool>* request) {
+ if (db_ && !request->IsCancelled()) {
+ db_->SetWebAppHasAllImages(request->GetArgument1(),
+ request->GetArgument2());
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::RemoveWebAppImpl(GenericRequest<GURL>* request) {
+ if (db_ && !request->IsCancelled()) {
+ db_->RemoveWebApp(request->GetArgument());
+ ScheduleCommit();
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::GetWebAppImagesImpl(GenericRequest<GURL>* request) {
+ if (db_ && !request->IsCancelled()) {
+ WDAppImagesResult result;
+ result.has_all_images = db_->GetWebAppHasAllImages(request->GetArgument());
+ db_->GetWebAppImages(request->GetArgument(), &result.images);
+ request->SetResult(
+ new WDResult<WDAppImagesResult>(WEB_APP_IMAGES, result));
+ }
+ request->RequestComplete();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataRequest implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+WebDataService::WebDataRequest::WebDataRequest(WebDataService* service,
+ Handle handle,
+ WebDataServiceConsumer* consumer)
+ : service_(service),
+ handle_(handle),
+ canceled_(false),
+ consumer_(consumer),
+ result_(NULL) {
+ message_loop_ = MessageLoop::current();
+}
+
+WebDataService::WebDataRequest::~WebDataRequest() {
+ delete result_;
+}
+
+WebDataService::Handle WebDataService::WebDataRequest::GetHandle() const {
+ return handle_;
+}
+
+WebDataServiceConsumer* WebDataService::WebDataRequest::GetConsumer() const {
+ return consumer_;
+}
+
+bool WebDataService::WebDataRequest::IsCancelled() const {
+ return canceled_;
+}
+
+void WebDataService::WebDataRequest::Cancel() {
+ canceled_ = true;
+ consumer_ = NULL;
+}
+
+void WebDataService::WebDataRequest::SetResult(WDTypedResult* r) {
+ result_ = r;
+}
+
+const WDTypedResult* WebDataService::WebDataRequest::GetResult() const {
+ return result_;
+}
+
+void WebDataService::WebDataRequest::RequestComplete() {
+ WebDataService* s = service_;
+ Task* t = NewRunnableMethod(s,
+ &WebDataService::RequestCompleted,
+ handle_);
+ message_loop_->PostTask(FROM_HERE, t);
+}
+
+int WebDataService::GetNextRequestHandle() {
+ AutoLock l(pending_lock_);
+ return ++next_request_handle_;
+}
diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h
new file mode 100644
index 0000000..a233338
--- /dev/null
+++ b/chrome/browser/webdata/web_data_service.h
@@ -0,0 +1,508 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__
+#define CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "chrome/browser/webdata/web_database.h"
+#include "chrome/common/scoped_vector.h"
+#include <map>
+
+class GURL;
+struct PasswordForm;
+struct IE7PasswordInfo;
+class ShutdownTask;
+class TemplateURL;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataService is a generic data repository for meta data associated with
+// web pages. All data is retrieved and archived in an asynchronous way.
+//
+// All requests return a handle. The handle can be used to cancel the request.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataService results
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Result types
+//
+typedef enum {
+ BOOL_RESULT = 1, // WDResult<bool>
+ KEYWORDS_RESULT, // WDResult<WDKeywordsResult>
+ INT64_RESULT, // WDResult<int64>
+ PASSWORD_RESULT, // WDResult<std::vector<PasswordForm*>>
+ PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo>
+ WEB_APP_IMAGES, // WDResult<WDAppImagesResult>
+} WDResultType;
+
+// Result from GetWebAppImages.
+struct WDAppImagesResult {
+ WDAppImagesResult() : has_all_images(false) {}
+
+ // True if SetWebAppHasAllImages(true) was invoked.
+ bool has_all_images;
+
+ // The images, may be empty.
+ std::vector<SkBitmap> images;
+};
+
+struct WDKeywordsResult {
+ std::vector<TemplateURL*> keywords;
+ // Identifies the ID of the TemplateURL that is the default search. A value of
+ // 0 indicates there is no default search provider.
+ int64 default_search_provider_id;
+ // Version of the builin keywords. A value of 0 indicates a first run.
+ int builtin_keyword_version;
+};
+
+//
+// The top level class for a result.
+//
+class WDTypedResult {
+ public:
+ virtual ~WDTypedResult() {}
+
+ // Return the result type.
+ WDResultType GetType() const {
+ return type_;
+ }
+
+ protected:
+ WDTypedResult(WDResultType type) : type_(type) {
+ }
+
+ private:
+ WDResultType type_;
+ DISALLOW_EVIL_CONSTRUCTORS(WDTypedResult);
+};
+
+// A result containing one specific pointer or literal value.
+template <class T> class WDResult : public WDTypedResult {
+ public:
+
+ WDResult(WDResultType type, T v) : WDTypedResult(type), value_(v) {
+ }
+
+ virtual ~WDResult() {
+ }
+
+ // Return a single value result.
+ T GetValue() const {
+ return value_;
+ }
+
+ private:
+ T value_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WDResult);
+};
+
+template <class T> class WDObjectResult : public WDTypedResult {
+ public:
+ explicit WDObjectResult(WDResultType type) : WDTypedResult(type) {
+ }
+
+ T* GetValue() const {
+ return &value_;
+ }
+
+ private:
+ // mutable to keep GetValue() const.
+ mutable T value_;
+ DISALLOW_EVIL_CONSTRUCTORS(WDObjectResult);
+};
+
+class WebDataServiceConsumer;
+
+class WebDataService : public base::RefCountedThreadSafe<WebDataService> {
+ public:
+
+ // All requests return an opaque handle of the following type.
+ typedef int Handle;
+
+ WebDataService();
+ ~WebDataService();
+
+ // Initializes the web data service. Returns false on failure
+ // Takes the path of the profile directory as its argument.
+ bool Init(const std::wstring& profile_path);
+
+ // Shutdown the web data service. The service can no longer be used after this
+ // call.
+ void Shutdown();
+
+ // Returns false if Shutdown() has been called.
+ bool IsRunning();
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Internal requests
+ //
+ // Every request is processed using a request object. The object contains
+ // both the request parameters and the results.
+ //////////////////////////////////////////////////////////////////////////////
+ class WebDataRequest {
+ public:
+ WebDataRequest(WebDataService* service,
+ Handle handle,
+ WebDataServiceConsumer* consumer);
+
+ virtual ~WebDataRequest();
+
+ Handle GetHandle() const;
+ WebDataServiceConsumer* GetConsumer() const;
+ bool IsCancelled() const;
+
+ // This can be invoked from any thread. From this point we assume that
+ // our consumer_ reference is invalid.
+ void Cancel();
+
+ // Invoked by the service when this request has been completed.
+ // This will notify the service in whatever thread was used to create this
+ // request.
+ void RequestComplete();
+
+ // The result is owned by the request.
+ void SetResult(WDTypedResult* r);
+ const WDTypedResult* GetResult() const;
+
+ private:
+ scoped_refptr<WebDataService> service_;
+ MessageLoop* message_loop_;
+ Handle handle_;
+ bool canceled_;
+ WebDataServiceConsumer* consumer_;
+ WDTypedResult* result_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WebDataRequest);
+ };
+
+ //
+ // Internally we use instances of the following template to represent
+ // requests.
+ //
+ template <class T>
+ class GenericRequest : public WebDataRequest {
+ public:
+ GenericRequest(WebDataService* service,
+ Handle handle,
+ WebDataServiceConsumer* consumer,
+ T arg) : WebDataRequest(service, handle, consumer),
+ arg_(arg) {
+ }
+
+ virtual ~GenericRequest() {
+ }
+
+ T GetArgument() {
+ return arg_;
+ }
+
+ private:
+ T arg_;
+ };
+
+ template <class T, class U>
+ class GenericRequest2 : public WebDataRequest {
+ public:
+ GenericRequest2(WebDataService* service,
+ Handle handle,
+ WebDataServiceConsumer* consumer,
+ T arg1,
+ U arg2)
+ : WebDataRequest(service, handle, consumer),
+ arg1_(arg1),
+ arg2_(arg2) {
+ }
+
+ virtual ~GenericRequest2() { }
+
+ T GetArgument1() {
+ return arg1_;
+ }
+
+ U GetArgument2() {
+ return arg2_;
+ }
+
+ private:
+ T arg1_;
+ U arg2_;
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Keywords
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // As the database processes requests at a later date, all deletion is
+ // done on the background thread.
+ //
+ // Many of the keyword related methods do not return a handle. This is because
+ // the caller (TemplateURLModel) does not need to know when the request is
+ // done.
+ void AddKeyword(const TemplateURL& url);
+
+ void RemoveKeyword(const TemplateURL& url);
+
+ void UpdateKeyword(const TemplateURL& url);
+
+ // Fetches the keywords.
+ // On success, consumer is notified with WDResult<std::vector<TemplateURL*>.
+ Handle GetKeywords(WebDataServiceConsumer* consumer);
+
+ // Sets the keywords used for the default search provider.
+ void SetDefaultSearchProvider(const TemplateURL* url);
+
+ // Sets the version of the builtin keywords.
+ void SetBuiltinKeywordVersion(int version);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Web Apps
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Sets the image for the specified web app. A web app can have any number of
+ // images, but only one at a particular size. If there was an image for the
+ // web app at the size of the given image it is replaced.
+ void SetWebAppImage(const GURL& app_url, const SkBitmap& image);
+
+ // Sets whether all the images have been downloaded for the specified web app.
+ void SetWebAppHasAllImages(const GURL& app_url, bool has_all_images);
+
+ // Removes all images for the specified web app.
+ void RemoveWebApp(const GURL& app_url);
+
+ // Fetches the images and whether all images have been downloaded for the
+ // specified web app.
+ Handle GetWebAppImages(const GURL& app_url, WebDataServiceConsumer* consumer);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Password manager
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Updates the remembered password form.
+ void UpdateLogin(const PasswordForm& form);
+
+ // Adds |form| to the list of remembered password forms.
+ void AddLogin(const PasswordForm& form);
+
+ // Adds |info| to the list of imported passwords from ie7/ie8.
+ void AddIE7Login(const IE7PasswordInfo& info);
+
+ // Removes |form| from the list of remembered password forms.
+ void RemoveLogin(const PasswordForm& form);
+
+ // Removes |info| from the list of imported passwords from ie7/ie8.
+ void RemoveIE7Login(const IE7PasswordInfo& info);
+
+ // Removes all logins created in the specified daterange
+ void RemoveLoginsCreatedBetween(const Time delete_begin,
+ const Time delete_end);
+
+ // Removes all logins created on or after the date passed in.
+ void RemoveLoginsCreatedAfter(const Time delete_begin);
+
+ // Gets a list of password forms that match |form|.
+ // |consumer| will be notified when the request is done. The result is of
+ // type WDResult<std::vector<PasswordForm*>>.
+ // The result will be null on failure. The |consumer| owns all PasswordForm's.
+ Handle GetLogins(const PasswordForm& form, WebDataServiceConsumer* consumer);
+
+ // Get the login matching the information in |info|. |consumer| will be
+ // notified when the request is done. The result is of type
+ // WDResult<IE7PasswordInfo>.
+ // If there is no match, the fields of the IE7PasswordInfo will be empty.
+ Handle GetIE7Login(const IE7PasswordInfo& info,
+ WebDataServiceConsumer* consumer);
+
+ // Gets the complete list of password forms that have not been blacklisted and
+ // are thus auto-fillable.
+ // |consumer| will be notified when the request is done. The result is of
+ // type WDResult<std::vector<PasswordForm*>>.
+ // The result will be null on failure. The |consumer| owns all PasswordForms.
+ Handle GetAllAutofillableLogins(WebDataServiceConsumer* consumer);
+
+ // Gets the complete list of password forms.
+ // |consumer| will be notified when the request is done. The result is of
+ // type WDResult<std::vector<PasswordForm*>>.
+ // The result will be null on failure. The |consumer| owns all PasswordForm's.
+ Handle GetAllLogins(WebDataServiceConsumer* consumer);
+
+ // Cancel any pending request. You need to call this method if your
+ // WebDataServiceConsumer is about to be deleted.
+ void CancelRequest(Handle h);
+
+ protected:
+ friend class TemplateURLModelTest;
+ friend class TemplateURLModelTestingProfile;
+ friend class WebDataServiceTest;
+ friend class WebDataRequest;
+
+ // This is invoked by the unit test; path is the path of the Web Data file.
+ bool WebDataService::InitWithPath(const std::wstring& path);
+
+ // Invoked by request implementations when a request has been processed.
+ void RequestCompleted(Handle h);
+
+ // Register the request as a pending request.
+ void RegisterRequest(WebDataRequest* request);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // The following methods are only invoked in the web data service thread.
+ //
+ //////////////////////////////////////////////////////////////////////////////
+ private:
+ friend class ShutdownTask;
+
+ typedef GenericRequest2<std::vector<const TemplateURL*>,
+ std::vector<TemplateURL*>> SetKeywordsRequest;
+
+ // Initialize the database with the provided path.
+ void InitializeDatabase(const std::wstring& path);
+
+ // Commit any pending transaction and deletes the database.
+ void WebDataService::ShutdownDatabase();
+
+ // Commit the current transaction and creates a new one.
+ void Commit();
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Keywords.
+ //
+ //////////////////////////////////////////////////////////////////////////////
+ void AddKeywordImpl(GenericRequest<TemplateURL>* request);
+ void RemoveKeywordImpl(GenericRequest<TemplateURL::IDType>* request);
+ void UpdateKeywordImpl(GenericRequest<TemplateURL>* request);
+ void GetKeywordsImpl(WebDataRequest* request);
+ void SetDefaultSearchProviderImpl(GenericRequest<TemplateURL::IDType>* r);
+ void SetBuiltinKeywordVersionImpl(GenericRequest<int>* r);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Password manager.
+ //
+ //////////////////////////////////////////////////////////////////////////////
+ void AddLoginImpl(GenericRequest<PasswordForm>* request);
+ void AddIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request);
+ void UpdateLoginImpl(GenericRequest<PasswordForm>* request);
+ void RemoveLoginImpl(GenericRequest<PasswordForm>* request);
+ void RemoveIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request);
+ void RemoveLoginsCreatedBetweenImpl(GenericRequest2<Time, Time>* request);
+ void GetLoginsImpl(GenericRequest<PasswordForm>* request);
+ void GetIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request);
+ void GetAllAutofillableLoginsImpl(WebDataRequest* request);
+ void GetAllLoginsImpl(WebDataRequest* request);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Web Apps.
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ void SetWebAppImageImpl(GenericRequest2<GURL,SkBitmap>* request);
+
+ void SetWebAppHasAllImagesImpl(GenericRequest2<GURL,bool>* request);
+
+ void RemoveWebAppImpl(GenericRequest<GURL>* request);
+
+ void GetWebAppImagesImpl(GenericRequest<GURL>* request);
+
+ Thread* thread() { return thread_; }
+
+ private:
+
+ // Schedule a task on our worker thread.
+ void ScheduleTask(Task* t);
+
+ // Schedule a commit if one is not already pending.
+ void ScheduleCommit();
+
+ // Return the next request handle.
+ int GetNextRequestHandle();
+
+ // Our worker thread. All requests are processed from that thread.
+ Thread* thread_;
+
+ // Our database.
+ WebDatabase* db_;
+
+ // Whether we should commit the database.
+ bool should_commit_;
+
+ // A lock to protect pending requests and next request handle.
+ Lock pending_lock_;
+
+ // Next handle to be used for requests. Incremented for each use.
+ Handle next_request_handle_;
+
+ typedef std::map<Handle, WebDataRequest*> RequestMap;
+ RequestMap pending_requests_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WebDataService);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataServiceConsumer.
+//
+// All requests to the web data service are asynchronous. When the request has
+// been performed, the data consumer is notified using the following interface.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class WebDataServiceConsumer {
+ public:
+
+ // Called when a request is done. h uniquely identifies the request.
+ // result can be NULL, if no result is expected or if the database could
+ // not be opened. The result object is destroyed after this call.
+ virtual void OnWebDataServiceRequestDone(WebDataService::Handle h,
+ const WDTypedResult* result) = 0;
+};
+
+#endif // CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__
diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc
new file mode 100644
index 0000000..60b6b82
--- /dev/null
+++ b/chrome/browser/webdata/web_database.cc
@@ -0,0 +1,873 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+#include <limits>
+
+#include "chrome/browser/webdata/web_database.h"
+
+#include "base/gfx/png_decoder.h"
+#include "base/gfx/png_encoder.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "chrome/browser/history/history_database.h"
+#include "chrome/browser/ie7_password.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/encryptor.h"
+#include "chrome/common/scoped_vector.h"
+#include "webkit/glue/password_form.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Schema
+//
+// keywords Most of the columns mirror that of a field in
+// TemplateURL. See TemplateURL for more details.
+// id
+// short_name
+// keyword
+// favicon_url
+// url
+// show_in_default_list
+// safe_for_autoreplace
+// originating_url
+// date_created This column was added after we allowed keywords.
+// Keywords created before we started tracking
+// creation date have a value of 0 for this.
+// usage_count
+// input_encodings Semicolon separated list of supported input
+// encodings, may be empty.
+// suggest_url
+// prepopulate_id See TemplateURL::prepoulate_id.
+//
+// logins
+// origin_url
+// action_url
+// username_element
+// username_value
+// password_element
+// password_value
+// submit_element
+// signon_realm The authority (scheme, host, port).
+// ssl_valid SSL status of page containing the form at first
+// impression.
+// preferred MRU bit.
+// date_created This column was added after logins support. "Legacy"
+// entries have a value of 0.
+// blacklisted_by_user Tracks whether or not the user opted to 'never
+// remember'
+// passwords for this site.
+//
+// web_app_icons
+// url URL of the web app.
+// width Width of the image.
+// height Height of the image.
+// image PNG encoded image data.
+//
+// web_apps
+// url URL of the web app.
+// has_all_images Do we have all the images?
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Current version number.
+static const int kCurrentVersionNumber = 20;
+
+// Keys used in the meta table.
+static const char* kDefaultSearchProviderKey = "Default Search Provider ID";
+static const char* kBuiltinKeywordVersion = "Builtin Keyword Version";
+
+std::string JoinStrings(const std::string& separator,
+ const std::vector<std::string>& strings) {
+ if (strings.empty())
+ return std::string();
+ std::vector<std::string>::const_iterator i(strings.begin());
+ std::string result(*i);
+ while(++i != strings.end())
+ result += separator + *i;
+ return result;
+}
+
+WebDatabase::WebDatabase() : db_(NULL), transaction_nesting_(0) {
+}
+
+WebDatabase::~WebDatabase() {
+ if (db_) {
+ DCHECK(transaction_nesting_ == 0) <<
+ "Forgot to close the transaction on shutdown";
+ sqlite3_close(db_);
+ db_ = NULL;
+ }
+}
+
+void WebDatabase::BeginTransaction() {
+ DCHECK(db_);
+ if (transaction_nesting_ == 0) {
+ int rv = sqlite3_exec(db_, "BEGIN TRANSACTION", NULL, NULL, NULL);
+ DCHECK(rv == SQLITE_OK) << "Failed to begin transaction";
+ }
+ transaction_nesting_++;
+}
+
+void WebDatabase::CommitTransaction() {
+ DCHECK(db_);
+ DCHECK(transaction_nesting_ > 0) << "Committing too many transaction";
+ transaction_nesting_--;
+ if (transaction_nesting_ == 0) {
+ int rv = sqlite3_exec(db_, "COMMIT", NULL, NULL, NULL);
+ DCHECK(rv == SQLITE_OK) << "Failed to commit transaction";
+ }
+}
+
+bool WebDatabase::Init(const std::wstring& db_name) {
+ // Open the database, using the narrow version of open so that
+ // the DB is in UTF-8.
+ if (sqlite3_open(WideToUTF8(db_name).c_str(), &db_) != SQLITE_OK) {
+ LOG(WARNING) << "Unable to open the web database.";
+ return false;
+ }
+
+ // We don't store that much data in the tables so use a small page size.
+ // This provides a large benefit for empty tables (which is very likely with
+ // the tables we create).
+ sqlite3_exec(db_, "PRAGMA page_size=2048", NULL, NULL, NULL);
+
+ // We shouldn't have much data and what access we currently have is quite
+ // infrequent. So we go with a small cache size.
+ sqlite3_exec(db_, "PRAGMA cache_size=32", NULL, NULL, NULL);
+
+ // Run the database in exclusive mode. Nobody else should be accessing the
+ // database while we're running, and this will give somewhat improved perf.
+ sqlite3_exec(db_, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL, NULL);
+
+ // Initialize various tables
+ SQLTransaction transaction(db_);
+ transaction.Begin();
+
+ // Version check.
+ if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db_))
+ return false;
+ if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
+ LOG(WARNING) << "Web database is too new";
+ return false;
+ }
+
+ // Initialize the tables.
+ if (!InitKeywordsTable() || !InitLoginsTable() || !InitWebAppIconsTable() ||
+ !InitWebAppsTable()) {
+ LOG(WARNING) << "Unable to initialize the web database.";
+ return false;
+ }
+ int cur_version = meta_table_.GetVersionNumber();
+
+ // Put migration code here.
+
+ // When the version is too old, we just try to continue anyway, there should
+ // not be a released product that makes a database too old for us to handle.
+ LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
+ "Web database version " << cur_version << " is too old to handle.";
+
+ return (transaction.Commit() == SQLITE_OK);
+}
+
+bool WebDatabase::SetWebAppImage(const GURL& url,
+ const SkBitmap& image) {
+ SQLStatement s;
+ if (s.prepare(db_,
+ "INSERT OR REPLACE INTO web_app_icons "
+ "(url, width, height, image) VALUES (?, ?, ?, ?)")
+ != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ std::vector<unsigned char> image_data;
+
+ SkAutoLockPixels pixel_lock(image);
+ PNGEncoder::Encode(reinterpret_cast<unsigned char*>(image.getPixels()),
+ PNGEncoder::FORMAT_BGRA, image.width(),
+ image.height(), image.width() * 4, false, &image_data);
+
+ s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ s.bind_int(1, image.width());
+ s.bind_int(2, image.height());
+ s.bind_blob(3, &image_data.front(), static_cast<int>(image_data.size()));
+ return s.step() == SQLITE_DONE;
+}
+
+bool WebDatabase::GetWebAppImages(const GURL& url,
+ std::vector<SkBitmap>* images) {
+ SQLStatement s;
+ if (s.prepare(db_, "SELECT image FROM web_app_icons WHERE url=?") !=
+ SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ while (s.step() == SQLITE_ROW) {
+ SkBitmap image;
+ std::vector<unsigned char> image_data;
+ s.column_blob_as_vector(0, &image_data);
+ if (PNGDecoder::Decode(&image_data, &image)) {
+ images->push_back(image);
+ } else {
+ // Should only have valid image data in the db.
+ NOTREACHED();
+ }
+ }
+ return true;
+}
+
+bool WebDatabase::SetWebAppHasAllImages(const GURL& url,
+ bool has_all_images) {
+ SQLStatement s;
+ if (s.prepare(db_, "INSERT OR REPLACE INTO web_apps (url, has_all_images) "
+ "VALUES (?, ?)") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ s.bind_int(1, has_all_images ? 1 : 0);
+ return (s.step() == SQLITE_DONE);
+}
+
+bool WebDatabase::GetWebAppHasAllImages(const GURL& url) {
+ SQLStatement s;
+ if (s.prepare(db_, "SELECT has_all_images FROM web_apps "
+ "WHERE url=?") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ return (s.step() == SQLITE_ROW && s.column_int(0) == 1);
+}
+
+bool WebDatabase::RemoveWebApp(const GURL& url) {
+ SQLStatement delete_s;
+ if (delete_s.prepare(db_, "DELETE FROM web_app_icons WHERE url = ?") !=
+ SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ delete_s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ if (delete_s.step() != SQLITE_DONE)
+ return false;
+
+ SQLStatement delete_s2;
+ if (delete_s2.prepare(db_, "DELETE FROM web_apps WHERE url = ?") !=
+ SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ delete_s2.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url));
+ return (delete_s2.step() == SQLITE_DONE);
+}
+
+bool WebDatabase::InitKeywordsTable() {
+ if (!DoesSqliteTableExist(db_, "keywords")) {
+ if (sqlite3_exec(db_, "CREATE TABLE keywords ("
+ "id INTEGER PRIMARY KEY,"
+ "short_name VARCHAR NOT NULL,"
+ "keyword VARCHAR NOT NULL,"
+ "favicon_url VARCHAR NOT NULL,"
+ "url VARCHAR NOT NULL,"
+ "show_in_default_list INTEGER,"
+ "safe_for_autoreplace INTEGER,"
+ "originating_url VARCHAR,"
+ "date_created INTEGER DEFAULT 0,"
+ "usage_count INTEGER DEFAULT 0,"
+ "input_encodings VARCHAR,"
+ "suggest_url VARCHAR,"
+ "prepopulate_id INTEGER DEFAULT 0)",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WebDatabase::InitLoginsTable() {
+ if (!DoesSqliteTableExist(db_, "logins")) {
+ // First time
+ if (sqlite3_exec(db_, "CREATE TABLE logins ("
+ "origin_url VARCHAR NOT NULL, "
+ "action_url VARCHAR, "
+ "username_element VARCHAR, "
+ "username_value VARCHAR, "
+ "password_element VARCHAR, "
+ "password_value BLOB, "
+ "submit_element VARCHAR, "
+ "signon_realm VARCHAR NOT NULL,"
+ "ssl_valid INTEGER NOT NULL,"
+ "preferred INTEGER NOT NULL,"
+ "date_created INTEGER NOT NULL,"
+ "blacklisted_by_user INTEGER NOT NULL,"
+ "scheme INTEGER NOT NULL,"
+ "UNIQUE "
+ "(origin_url, username_element, "
+ "username_value, password_element, "
+ "submit_element, signon_realm))",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ if (sqlite3_exec(db_, "CREATE INDEX logins_signon ON "
+ "logins (signon_realm)",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (!DoesSqliteTableExist(db_, "ie7_logins")) {
+ // First time
+ if (sqlite3_exec(db_, "CREATE TABLE ie7_logins ("
+ "url_hash VARCHAR NOT NULL, "
+ "password_value BLOB, "
+ "date_created INTEGER NOT NULL,"
+ "UNIQUE "
+ "(url_hash))",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ if (sqlite3_exec(db_, "CREATE INDEX ie7_logins_hash ON "
+ "ie7_logins (url_hash)",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WebDatabase::InitWebAppIconsTable() {
+ if (!DoesSqliteTableExist(db_, "web_app_icons")) {
+ if (sqlite3_exec(db_, "CREATE TABLE web_app_icons ("
+ "url LONGVARCHAR,"
+ "width int,"
+ "height int,"
+ "image BLOB, UNIQUE (url, width, height))",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WebDatabase::InitWebAppsTable() {
+ if (!DoesSqliteTableExist(db_, "web_apps")) {
+ if (sqlite3_exec(db_, "CREATE TABLE web_apps ("
+ "url LONGVARCHAR UNIQUE,"
+ "has_all_images INTEGER NOT NULL)",
+ NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ if (sqlite3_exec(db_, "CREATE INDEX web_apps_url_index ON "
+ "web_apps (url)", NULL, NULL, NULL) != SQLITE_OK) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+static void BindURLToStatement(const TemplateURL& url, SQLStatement* s) {
+ s->bind_wstring(0, url.short_name());
+ s->bind_wstring(1, url.keyword());
+ GURL favicon_url = url.GetFavIconURL();
+ if (!favicon_url.is_valid()) {
+ s->bind_string(2, "");
+ } else {
+ s->bind_string(2, history::HistoryDatabase::GURLToDatabaseURL(
+ url.GetFavIconURL()));
+ }
+ if (url.url())
+ s->bind_wstring(3, url.url()->url());
+ else
+ s->bind_wstring(3, std::wstring());
+ s->bind_int(4, url.safe_for_autoreplace() ? 1 : 0);
+ if (!url.originating_url().is_valid()) {
+ s->bind_string(5, std::string());
+ } else {
+ s->bind_string(5, history::HistoryDatabase::GURLToDatabaseURL(
+ url.originating_url()));
+ }
+ s->bind_int64(6, url.date_created().ToTimeT());
+ s->bind_int(7, url.usage_count());
+ s->bind_string(8, JoinStrings(";", url.input_encodings()));
+ s->bind_int(9, url.show_in_default_list() ? 1 : 0);
+ if (url.suggestions_url())
+ s->bind_wstring(10, url.suggestions_url()->url());
+ else
+ s->bind_wstring(10, std::wstring());
+ s->bind_int(11, url.prepopulate_id());
+}
+
+bool WebDatabase::AddKeyword(const TemplateURL& url) {
+ DCHECK(url.id());
+ SQLStatement s;
+ if (s.prepare(db_,
+ "INSERT INTO keywords "
+ "(short_name, keyword, favicon_url, url, safe_for_autoreplace, "
+ "originating_url, date_created, usage_count, input_encodings, "
+ "show_in_default_list, suggest_url, prepopulate_id, id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ BindURLToStatement(url, &s);
+ s.bind_int64(12, url.id());
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::RemoveKeyword(TemplateURL::IDType id) {
+ DCHECK(id);
+ SQLStatement s;
+ if (s.prepare(db_,
+ "DELETE FROM keywords WHERE id = ?") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_int64(0, id);
+ return s.step() == SQLITE_DONE;
+}
+
+bool WebDatabase::GetKeywords(std::vector<TemplateURL*>* urls) {
+ SQLStatement s;
+ if (s.prepare(db_,
+ "SELECT id, short_name, keyword, favicon_url, url, "
+ "safe_for_autoreplace, originating_url, date_created, "
+ "usage_count, input_encodings, show_in_default_list, "
+ "suggest_url, prepopulate_id "
+ "FROM keywords ORDER BY id ASC") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ int64 result;
+ while ((result = s.step()) == SQLITE_ROW) {
+ TemplateURL* template_url = new TemplateURL();
+ std::wstring tmp;
+ template_url->set_id(s.column_int64(0));
+
+ s.column_string16(1, &tmp);
+ DCHECK(!tmp.empty());
+ template_url->set_short_name(tmp);
+
+ s.column_string16(2, &tmp);
+ template_url->set_keyword(tmp);
+
+ s.column_string16(3, &tmp);
+ if (!tmp.empty())
+ template_url->SetFavIconURL(GURL(tmp));
+
+ s.column_string16(4, &tmp);
+ template_url->SetURL(tmp, 0, 0);
+
+ template_url->set_safe_for_autoreplace(s.column_int(5) == 1);
+
+ s.column_string16(6, &tmp);
+ if (!tmp.empty())
+ template_url->set_originating_url(GURL(tmp));
+
+ template_url->set_date_created(Time::FromTimeT(s.column_int64(7)));
+
+ template_url->set_usage_count(s.column_int(8));
+
+ std::vector<std::string> encodings;
+ SplitString(s.column_string(9), ';', &encodings);
+ template_url->set_input_encodings(encodings);
+
+ template_url->set_show_in_default_list(s.column_int(10) == 1);
+
+ s.column_string16(11, &tmp);
+ template_url->SetSuggestionsURL(tmp, 0, 0);
+
+ template_url->set_prepopulate_id(s.column_int(12));
+
+ urls->push_back(template_url);
+ }
+ return result == SQLITE_DONE;
+}
+
+bool WebDatabase::UpdateKeyword(const TemplateURL& url) {
+ DCHECK(url.id());
+ SQLStatement s;
+ if (s.prepare(db_,
+ "UPDATE keywords "
+ "SET short_name=?, keyword=?, favicon_url=?, url=?,"
+ "safe_for_autoreplace=?, originating_url=?, "
+ "date_created=?, usage_count=?, input_encodings=?, "
+ "show_in_default_list=?, suggest_url=?, prepopulate_id=? "
+ "WHERE id=?")
+ != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ BindURLToStatement(url, &s);
+ s.bind_int64(12, url.id());
+ return s.step() == SQLITE_DONE;
+}
+
+bool WebDatabase::SetDefaultSearchProviderID(int64 id) {
+ return meta_table_.SetValue(kDefaultSearchProviderKey, id);
+}
+
+int64 WebDatabase::GetDefaulSearchProviderID() {
+ int64 value = 0;
+ meta_table_.GetValue(kDefaultSearchProviderKey, &value);
+ return value;
+}
+
+bool WebDatabase::SetBuitinKeywordVersion(int version) {
+ return meta_table_.SetValue(kBuiltinKeywordVersion, version);
+}
+
+int WebDatabase::GetBuitinKeywordVersion() {
+ int version = 0;
+ meta_table_.GetValue(kBuiltinKeywordVersion, &version);
+ return version;
+}
+
+// Return a new GURL like url, but without any "#foo" bit on the end.
+static GURL GURLWithoutRef(const GURL& url) {
+ url_canon::Replacements<char> replacements;
+ replacements.ClearRef();
+ return url.ReplaceComponents(replacements);
+}
+
+// Convert a list of GUIDs from the in-memory form to the form we keep in
+// the database (tab-separated string).
+static std::string SerializeGUIDs(const std::vector<std::string>& guids) {
+ std::string result;
+ for (size_t i = 0; i < guids.size(); ++i) {
+ if (!result.empty())
+ result.push_back('\t');
+ const std::string& guid = guids[i];
+ for (size_t j = 0; j < guid.size(); ++j) {
+ char ch = guid[j];
+ // If we have any embedded tabs in the GUID (a pathological case),
+ // we normalize them to spaces.
+ if (ch == '\t')
+ ch = ' ';
+ result.push_back(ch);
+ }
+ }
+ return result;
+}
+
+// The partner of SerializeGUIDs. Converts a serialized GUIDs string
+// back to a vector.
+static void DeserializeGUIDs(const std::string& str,
+ std::vector<std::string>* guids) {
+ SplitString(str, '\t', guids);
+}
+
+bool WebDatabase::AddLogin(const PasswordForm& form) {
+ SQLStatement s;
+ std::string encrypted_password;
+ if (s.prepare(db_,
+ "INSERT OR REPLACE INTO logins "
+ "(origin_url, action_url, username_element, username_value, "
+ " password_element, password_value, submit_element, "
+ " signon_realm, ssl_valid, preferred, date_created, "
+ " blacklisted_by_user, scheme) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s.bind_string(0, form.origin.spec());
+ s.bind_string(1, form.action.spec());
+ s.bind_wstring(2, form.username_element);
+ s.bind_wstring(3, form.username_value);
+ s.bind_wstring(4, form.password_element);
+ Encryptor::EncryptWideString(form.password_value, &encrypted_password);
+ s.bind_blob(5, encrypted_password.data(),
+ static_cast<int>(encrypted_password.length()));
+ s.bind_wstring(6, form.submit_element);
+ s.bind_string(7, form.signon_realm);
+ s.bind_int(8, form.ssl_valid);
+ s.bind_int(9, form.preferred);
+ s.bind_int64(10, form.date_created.ToTimeT());
+ s.bind_int(11, form.blacklisted_by_user);
+ s.bind_int(12, form.scheme);
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::AddIE7Login(const IE7PasswordInfo& info) {
+ SQLStatement s;
+ if (s.prepare(db_,
+ "INSERT OR REPLACE INTO ie7_logins "
+ "(url_hash, password_value, date_created) "
+ "VALUES (?, ?, ?)") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s.bind_wstring(0, info.url_hash);
+ s.bind_blob(1, &info.encrypted_data.front(),
+ static_cast<int>(info.encrypted_data.size()));
+ s.bind_int64(2, info.date_created.ToTimeT());
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::UpdateLogin(const PasswordForm& form) {
+ SQLStatement s;
+ std::string encrypted_password;
+ if (s.prepare(db_, "UPDATE logins SET "
+ "action_url = ?, "
+ "password_value = ?, "
+ "ssl_valid = ?, "
+ "preferred = ? "
+ "WHERE origin_url = ? AND "
+ "username_element = ? AND "
+ "username_value = ? AND "
+ "password_element = ? AND "
+ "signon_realm = ?") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s.bind_string(0, form.action.spec());
+ Encryptor::EncryptWideString(form.password_value, &encrypted_password);
+ s.bind_blob(1, encrypted_password.data(),
+ static_cast<int>(encrypted_password.length()));
+ s.bind_int(2, form.ssl_valid);
+ s.bind_int(3, form.preferred);
+ s.bind_string(4, form.origin.spec());
+ s.bind_wstring(5, form.username_element);
+ s.bind_wstring(6, form.username_value);
+ s.bind_wstring(7, form.password_element);
+ s.bind_string(8, form.signon_realm);
+
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::RemoveLogin(const PasswordForm& form) {
+ SQLStatement s;
+ // Remove a login by UNIQUE-constrained fields.
+ if (s.prepare(db_,
+ "DELETE FROM logins WHERE "
+ "origin_url = ? AND "
+ "username_element = ? AND "
+ "username_value = ? AND "
+ "password_element = ? AND "
+ "submit_element = ? AND "
+ "signon_realm = ? ") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_string(0, form.origin.spec());
+ s.bind_wstring(1, form.username_element);
+ s.bind_wstring(2, form.username_value);
+ s.bind_wstring(3, form.password_element);
+ s.bind_wstring(4, form.submit_element);
+ s.bind_string(5, form.signon_realm);
+
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::RemoveIE7Login(const IE7PasswordInfo& info) {
+ SQLStatement s;
+ // Remove a login by UNIQUE-constrained fields.
+ if (s.prepare(db_,
+ "DELETE FROM ie7_logins WHERE "
+ "url_hash = ?") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.bind_wstring(0, info.url_hash);
+
+ if (s.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool WebDatabase::RemoveLoginsCreatedBetween(const Time delete_begin,
+ const Time delete_end) {
+ SQLStatement s1;
+ if (s1.prepare(db_,
+ "DELETE FROM logins WHERE "
+ "date_created >= ? AND date_created < ?") != SQLITE_OK) {
+ NOTREACHED() << "Statement 1 prepare failed";
+ return false;
+ }
+ s1.bind_int64(0, delete_begin.ToTimeT());
+ s1.bind_int64(1,
+ delete_end.is_null() ?
+ std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ SQLStatement s2;
+ if (s2.prepare(db_,
+ "DELETE FROM ie7_logins WHERE "
+ "date_created >= ? AND date_created < ?") != SQLITE_OK) {
+ NOTREACHED() << "Statement 2 prepare failed";
+ return false;
+ }
+ s2.bind_int64(0, delete_begin.ToTimeT());
+ s2.bind_int64(1,
+ delete_end.is_null() ?
+ std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ return s1.step() == SQLITE_DONE && s2.step() == SQLITE_DONE;
+}
+
+static void InitPasswordFormFromStatement(PasswordForm* form,
+ SQLStatement* s) {
+ std::string encrypted_password;
+ std::string tmp;
+ s->column_string(0, &tmp);
+ form->origin = GURL(tmp);
+ s->column_string(1, &tmp);
+ form->action = GURL(tmp);
+ s->column_string16(2, &form->username_element);
+ s->column_string16(3, &form->username_value);
+ s->column_string16(4, &form->password_element);
+ s->column_blob_as_string(5, &encrypted_password);
+ Encryptor::DecryptWideString(encrypted_password, &form->password_value);
+ s->column_string16(6, &form->submit_element);
+ s->column_string(7, &tmp);
+ form->signon_realm = tmp;
+ form->ssl_valid = (s->column_int(8) > 0);
+ form->preferred = (s->column_int(9) > 0);
+ form->date_created = Time::FromTimeT(s->column_int64(10));
+ form->blacklisted_by_user = (s->column_int(11) > 0);
+ int scheme_int = s->column_int(12);
+ DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER));
+ form->scheme = static_cast<PasswordForm::Scheme>(scheme_int);
+}
+
+bool WebDatabase::GetLogins(const PasswordForm& form,
+ std::vector<PasswordForm*>* forms) {
+ DCHECK(forms);
+ SQLStatement s;
+ if (s.prepare(db_,
+ "SELECT origin_url, action_url, "
+ "username_element, username_value, "
+ "password_element, password_value, "
+ "submit_element, signon_realm, "
+ "ssl_valid, preferred, "
+ "date_created, blacklisted_by_user, scheme FROM logins "
+ "WHERE signon_realm == ? ") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s.bind_string(0, form.signon_realm);
+
+ int64 result;
+ while ((result = s.step()) == SQLITE_ROW) {
+ PasswordForm* new_form = new PasswordForm();
+ InitPasswordFormFromStatement(new_form, &s);
+
+ forms->push_back(new_form);
+ }
+ return result == SQLITE_DONE;
+}
+
+bool WebDatabase::GetIE7Login(const IE7PasswordInfo& info,
+ IE7PasswordInfo* result) {
+ DCHECK(result);
+ SQLStatement s;
+ if (s.prepare(db_,
+ "SELECT password_value, date_created FROM ie7_logins "
+ "WHERE url_hash == ? ") != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s.bind_wstring(0, info.url_hash);
+
+ int64 query_result = s.step();
+ if (query_result == SQLITE_ROW) {
+ s.column_blob_as_vector(0, &result->encrypted_data);
+ result->date_created = Time::FromTimeT(s.column_int64(1));
+ result->url_hash = info.url_hash;
+ s.step();
+ }
+ return query_result == SQLITE_DONE;
+}
+
+bool WebDatabase::GetAllLogins(std::vector<PasswordForm*>* forms,
+ bool include_blacklisted) {
+ DCHECK(forms);
+ SQLStatement s;
+ std::string stmt = "SELECT origin_url, action_url, "
+ "username_element, username_value, "
+ "password_element, password_value, "
+ "submit_element, signon_realm, ssl_valid, preferred, "
+ "date_created, blacklisted_by_user, scheme FROM logins ";
+ if (!include_blacklisted)
+ stmt.append("WHERE blacklisted_by_user == 0 ");
+ stmt.append("ORDER BY origin_url");
+
+ if (s.prepare(db_, stmt.c_str()) != SQLITE_OK) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ int64 result;
+ while ((result = s.step()) == SQLITE_ROW) {
+ PasswordForm* new_form = new PasswordForm();
+ InitPasswordFormFromStatement(new_form, &s);
+
+ forms->push_back(new_form);
+ }
+ return result == SQLITE_DONE;
+}
diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h
new file mode 100644
index 0000000..f34d570
--- /dev/null
+++ b/chrome/browser/webdata/web_database.h
@@ -0,0 +1,167 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_WEBDATA_WEB_DATABASE_H__
+#define CHROME_BROWSER_WEBDATA_WEB_DATABASE_H__
+
+#include <string>
+#include <vector>
+#include <hash_map>
+
+#include "base/basictypes.h"
+#include "chrome/browser/meta_table_helper.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/common/sqlite_utils.h"
+#include "skia/include/SkBitmap.h"
+
+class Time;
+struct PasswordForm;
+struct IE7PasswordInfo;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A Sqlite database instance to store all the meta data we have about web pages
+//
+////////////////////////////////////////////////////////////////////////////////
+class WebDatabase {
+ public:
+ WebDatabase();
+ ~WebDatabase();
+
+ // Initialize the database given a name. The name defines where the sqlite
+ // file is. If false is returned, no other method should be called.
+ bool Init(const std::wstring& db_name);
+
+ // Transactions management
+ void BeginTransaction();
+ void CommitTransaction();
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Keywords
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Adds a new keyword, updating the id field on success.
+ // Returns true if successful.
+ bool AddKeyword(const TemplateURL& url);
+
+ // Removes the specified keyword.
+ // Returns true if successful.
+ bool RemoveKeyword(TemplateURL::IDType id);
+
+ // Loads the keywords into the specified vector. It's up to the caller to
+ // delete the returned objects.
+ // Returns true on success.
+ bool GetKeywords(std::vector<TemplateURL*>* urls);
+
+ // Updates the database values for the specified url.
+ // Returns true on success.
+ bool UpdateKeyword(const TemplateURL& url);
+
+ // ID (TemplateURL->id) of the default search provider.
+ bool SetDefaultSearchProviderID(int64 id);
+ int64 GetDefaulSearchProviderID();
+
+ // Version of the builtin keywords.
+ bool SetBuitinKeywordVersion(int version);
+ int GetBuitinKeywordVersion();
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Password manager support
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Adds |form| to the list of remembered password forms.
+ bool AddLogin(const PasswordForm& form);
+
+ // Adds |info| to the list of imported passwords from ie7/ie8.
+ bool AddIE7Login(const IE7PasswordInfo& info);
+
+ // Updates remembered password form.
+ bool UpdateLogin(const PasswordForm& form);
+
+ // Removes |form| from the list of remembered password forms.
+ bool RemoveLogin(const PasswordForm& form);
+
+ // Removes |info| from the list of imported passwords from ie7/ie8.
+ bool RemoveIE7Login(const IE7PasswordInfo& info);
+
+ // Removes all logins created from |delete_begin| onwards (inclusive) and
+ // before |delete_end|. You may use a null Time value to do an unbounded
+ // delete in either direction.
+ bool RemoveLoginsCreatedBetween(const Time delete_begin,
+ const Time delete_end);
+
+ // Loads a list of matching password forms into the specified vector |forms|.
+ // The list will contain all possibly relevant entries to the observed |form|,
+ // including blacklisted matches.
+ bool GetLogins(const PasswordForm& form, std::vector<PasswordForm*>* forms);
+
+ // Return the ie7/ie8 login matching |info|.
+ bool GetIE7Login(const IE7PasswordInfo& info, IE7PasswordInfo* result);
+
+ // Loads the complete list of password forms into the specified vector |forms|
+ // if include_blacklisted is true, otherwise only loads those which are
+ // actually autofillable; i.e haven't been blacklisted by the user selecting
+ // the 'Never for this site' button.
+ bool GetAllLogins(std::vector<PasswordForm*>* forms,
+ bool include_blacklisted);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Web Apps
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ bool SetWebAppImage(const GURL& url, const SkBitmap& image);
+ bool GetWebAppImages(const GURL& url, std::vector<SkBitmap>* images);
+
+ bool SetWebAppHasAllImages(const GURL& url, bool has_all_images);
+ bool GetWebAppHasAllImages(const GURL& url);
+
+ bool RemoveWebApp(const GURL& url);
+
+ private:
+ friend class WebDatabaseTest;
+
+ bool InitKeywordsTable();
+ bool InitLoginsTable();
+ bool InitWebAppIconsTable();
+ bool InitWebAppsTable();
+
+ sqlite3* db_;
+ int transaction_nesting_;
+ MetaTableHelper meta_table_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WebDatabase);
+};
+
+#endif // CHROME_BROWSER_WEBDATA_WEB_DATABASE_H__
diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc
new file mode 100644
index 0000000..fd68974
--- /dev/null
+++ b/chrome/browser/webdata/web_database_unittest.cc
@@ -0,0 +1,587 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/webdata/web_database.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/stl_util-inl.h"
+#include "skia/include/SkBitmap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/password_form.h"
+
+class WebDatabaseTest : public testing::Test {
+ protected:
+
+ virtual void SetUp() {
+ wchar_t b[32];
+ _itow_s(static_cast<int>(GetTickCount()), b, arraysize(b), 10);
+
+ PathService::Get(chrome::DIR_TEST_DATA, &file_);
+ file_ += file_util::kPathSeparator;
+ file_ += L"TestWebDatabase";
+ file_ += b;
+ file_ += L".db";
+ DeleteFile(file_.c_str());
+ }
+
+ virtual void TearDown() {
+ DeleteFile(file_.c_str());
+ }
+
+ static int GetKeyCount(const DictionaryValue& d) {
+ DictionaryValue::key_iterator i(d.begin_keys());
+ DictionaryValue::key_iterator e(d.end_keys());
+
+ int r = 0;
+ while (i != e) {
+ ++i;
+ ++r;
+ }
+ return r;
+ }
+
+ static bool StringDictionaryValueEquals(const DictionaryValue& a,
+ const DictionaryValue& b) {
+ int a_count = 0;
+ int b_count = GetKeyCount(b);
+ DictionaryValue::key_iterator i(a.begin_keys());
+ DictionaryValue::key_iterator e(a.end_keys());
+ std::wstring av, bv;
+ while (i != e) {
+ if (!(a.GetString(*i, &av)) ||
+ !(b.GetString(*i, &bv)) ||
+ av != bv)
+ return false;
+
+ a_count++;
+ ++i;
+ }
+
+ return (a_count == b_count);
+ }
+
+ static int64 GetID(const TemplateURL* url) {
+ return url->id();
+ }
+
+ static void SetID(int64 new_id, TemplateURL* url) {
+ url->set_id(new_id);
+ }
+
+ static void set_prepopulate_id(TemplateURL* url, int id) {
+ url->set_prepopulate_id(id);
+ }
+
+ std::wstring file_;
+};
+
+TEST_F(WebDatabaseTest, Keywords) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ TemplateURL template_url;
+ template_url.set_short_name(L"short_name");
+ template_url.set_keyword(L"keyword");
+ GURL favicon_url("http://favicon.url");
+ GURL originating_url("http://google.com");
+ template_url.SetFavIconURL(favicon_url);
+ template_url.SetURL(L"url", 0, 0);
+ template_url.set_safe_for_autoreplace(true);
+ template_url.set_show_in_default_list(true);
+ template_url.set_originating_url(originating_url);
+ template_url.set_usage_count(32);
+ template_url.add_input_encoding("UTF-8");
+ set_prepopulate_id(&template_url, 10);
+ SetID(1, &template_url);
+
+ EXPECT_TRUE(db.AddKeyword(template_url));
+
+ std::vector<TemplateURL*> template_urls;
+ EXPECT_TRUE(db.GetKeywords(&template_urls));
+
+ EXPECT_EQ(1, template_urls.size());
+ const TemplateURL* restored_url = template_urls.front();
+
+ EXPECT_EQ(template_url.short_name(), restored_url->short_name());
+
+ EXPECT_EQ(template_url.keyword(), restored_url->keyword());
+
+ EXPECT_TRUE(favicon_url == restored_url->GetFavIconURL());
+
+ EXPECT_TRUE(restored_url->safe_for_autoreplace());
+
+ EXPECT_TRUE(restored_url->show_in_default_list());
+
+ EXPECT_EQ(GetID(&template_url), GetID(restored_url));
+
+ EXPECT_TRUE(originating_url == restored_url->originating_url());
+
+ EXPECT_EQ(32, restored_url->usage_count());
+
+ ASSERT_EQ(1, restored_url->input_encodings().size());
+ EXPECT_EQ("UTF-8", restored_url->input_encodings()[0]);
+
+ EXPECT_EQ(10, restored_url->prepopulate_id());
+
+ EXPECT_TRUE(db.RemoveKeyword(restored_url->id()));
+
+ template_urls.clear();
+ EXPECT_TRUE(db.GetKeywords(&template_urls));
+
+ EXPECT_EQ(0, template_urls.size());
+
+ delete restored_url;
+}
+
+TEST_F(WebDatabaseTest, KeywordMisc) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ ASSERT_EQ(0, db.GetDefaulSearchProviderID());
+ ASSERT_EQ(0, db.GetBuitinKeywordVersion());
+
+ db.SetDefaultSearchProviderID(10);
+ db.SetBuitinKeywordVersion(11);
+
+ ASSERT_EQ(10, db.GetDefaulSearchProviderID());
+ ASSERT_EQ(11, db.GetBuitinKeywordVersion());
+}
+
+TEST_F(WebDatabaseTest, UpdateKeyword) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ TemplateURL template_url;
+ template_url.set_short_name(L"short_name");
+ template_url.set_keyword(L"keyword");
+ GURL favicon_url("http://favicon.url");
+ GURL originating_url("http://originating.url");
+ template_url.SetFavIconURL(favicon_url);
+ template_url.SetURL(L"url", 0, 0);
+ template_url.set_safe_for_autoreplace(true);
+ template_url.set_show_in_default_list(true);
+ template_url.SetSuggestionsURL(L"url2", 0, 0);
+ SetID(1, &template_url);
+
+ EXPECT_TRUE(db.AddKeyword(template_url));
+
+ GURL originating_url2("http://originating.url");
+ template_url.set_keyword(L"X");
+ template_url.set_originating_url(originating_url2);
+ template_url.add_input_encoding("Shift_JIS");
+ set_prepopulate_id(&template_url, 5);
+ EXPECT_TRUE(db.UpdateKeyword(template_url));
+
+ std::vector<TemplateURL*> template_urls;
+ EXPECT_TRUE(db.GetKeywords(&template_urls));
+
+ EXPECT_EQ(1, template_urls.size());
+ const TemplateURL* restored_url = template_urls.front();
+
+ EXPECT_EQ(template_url.short_name(), restored_url->short_name());
+
+ EXPECT_EQ(template_url.keyword(), restored_url->keyword());
+
+ EXPECT_TRUE(favicon_url == restored_url->GetFavIconURL());
+
+ EXPECT_TRUE(restored_url->safe_for_autoreplace());
+
+ EXPECT_TRUE(restored_url->show_in_default_list());
+
+ EXPECT_EQ(GetID(&template_url), GetID(restored_url));
+
+ EXPECT_TRUE(originating_url2 == restored_url->originating_url());
+
+ ASSERT_EQ(1, restored_url->input_encodings().size());
+ ASSERT_EQ("Shift_JIS", restored_url->input_encodings()[0]);
+
+ EXPECT_EQ(template_url.suggestions_url()->url(),
+ restored_url->suggestions_url()->url());
+
+ EXPECT_EQ(template_url.id(), restored_url->id());
+
+ EXPECT_EQ(template_url.prepopulate_id(), restored_url->prepopulate_id());
+
+ delete restored_url;
+}
+
+TEST_F(WebDatabaseTest, KeywordWithNoFavicon) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ TemplateURL template_url;
+ template_url.set_short_name(L"short_name");
+ template_url.set_keyword(L"keyword");
+ template_url.SetURL(L"url", 0, 0);
+ template_url.set_safe_for_autoreplace(true);
+ SetID(-100, &template_url);
+
+ EXPECT_TRUE(db.AddKeyword(template_url));
+
+ std::vector<TemplateURL*> template_urls;
+ EXPECT_TRUE(db.GetKeywords(&template_urls));
+ EXPECT_EQ(1, template_urls.size());
+ const TemplateURL* restored_url = template_urls.front();
+
+ EXPECT_EQ(template_url.short_name(), restored_url->short_name());
+ EXPECT_EQ(template_url.keyword(), restored_url->keyword());
+ EXPECT_TRUE(!restored_url->GetFavIconURL().is_valid());
+ EXPECT_TRUE(restored_url->safe_for_autoreplace());
+ EXPECT_EQ(GetID(&template_url), GetID(restored_url));
+ delete restored_url;
+}
+
+TEST_F(WebDatabaseTest, Logins) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ std::vector<PasswordForm*> result;
+
+ // Verify the database is empty.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(0, result.size());
+
+ // Example password form.
+ PasswordForm form;
+ form.origin = GURL(L"http://www.google.com/accounts/LoginAuth");
+ form.action = GURL(L"http://www.google.com/accounts/Login");
+ form.username_element = L"Email";
+ form.username_value = L"test@gmail.com";
+ form.password_element = L"Passwd";
+ form.password_value = L"test";
+ form.submit_element = L"signIn";
+ form.signon_realm = "http://www.google.com";
+ form.ssl_valid = false;
+ form.preferred = false;
+ form.scheme = PasswordForm::SCHEME_HTML;
+
+ // Add it and make sure it is there.
+ EXPECT_TRUE(db.AddLogin(form));
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // Match against an exact copy.
+ EXPECT_TRUE(db.GetLogins(form, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // The example site changes...
+ PasswordForm form2(form);
+ form2.origin = GURL(L"http://www.google.com/new/accounts/LoginAuth");
+ form2.submit_element = L"reallySignIn";
+
+ // Match against an inexact copy
+ EXPECT_TRUE(db.GetLogins(form2, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // Uh oh, the site changed origin & action URL's all at once!
+ PasswordForm form3(form2);
+ form3.action = GURL(L"http://www.google.com/new/accounts/Login");
+
+ // signon_realm is the same, should match.
+ EXPECT_TRUE(db.GetLogins(form3, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // Imagine the site moves to a secure server for login.
+ PasswordForm form4(form3);
+ form4.signon_realm = "https://www.google.com";
+ form4.ssl_valid = true;
+
+ // We have only an http record, so no match for this.
+ EXPECT_TRUE(db.GetLogins(form4, &result));
+ EXPECT_EQ(0, result.size());
+
+ // Let's imagine the user logs into the secure site.
+ EXPECT_TRUE(db.AddLogin(form4));
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(2, result.size());
+ delete result[0];
+ delete result[1];
+ result.clear();
+
+ // Now the match works
+ EXPECT_TRUE(db.GetLogins(form4, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // The user chose to forget the original but not the new.
+ EXPECT_TRUE(db.RemoveLogin(form));
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // The old form wont match the new site (http vs https).
+ EXPECT_TRUE(db.GetLogins(form, &result));
+ EXPECT_EQ(0, result.size());
+
+ // The user's request for the HTTPS site is intercepted
+ // by an attacker who presents an invalid SSL cert.
+ PasswordForm form5(form4);
+ form5.ssl_valid = 0;
+
+ // It will match in this case.
+ EXPECT_TRUE(db.GetLogins(form5, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+
+ // User changes his password.
+ PasswordForm form6(form5);
+ form6.password_value = L"test6";
+ form6.preferred = true;
+
+ // We update, and check to make sure it matches the
+ // old form, and there is only one record.
+ EXPECT_TRUE(db.UpdateLogin(form6));
+ // matches
+ EXPECT_TRUE(db.GetLogins(form5, &result));
+ EXPECT_EQ(1, result.size());
+ delete result[0];
+ result.clear();
+ // Only one record.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(1, result.size());
+ // password element was updated.
+ EXPECT_EQ(form6.password_value, result[0]->password_value);
+ // Preferred login.
+ EXPECT_TRUE(form6.preferred);
+ delete result[0];
+ result.clear();
+
+ // Make sure everything can disappear.
+ EXPECT_TRUE(db.RemoveLogin(form4));
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(0, result.size());
+}
+
+static bool AddTimestampedLogin(WebDatabase* db, std::string url,
+ std::wstring unique_string, const Time& time) {
+ // Example password form.
+ PasswordForm form;
+ form.origin = GURL(url + std::string("/LoginAuth"));
+ form.username_element = unique_string.c_str();
+ form.username_value = unique_string.c_str();
+ form.password_element = unique_string.c_str();
+ form.submit_element = L"signIn";
+ form.signon_realm = url;
+ form.date_created = time;
+ return db->AddLogin(form);
+}
+
+static void ClearResults(std::vector<PasswordForm*>* results) {
+ for (size_t i = 0; i < results->size(); ++i) {
+ delete (*results)[i];
+ }
+ results->clear();
+}
+
+TEST_F(WebDatabaseTest, ClearPrivateData_SavedPasswords) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+
+ std::vector<PasswordForm*> result;
+
+ // Verify the database is empty.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(0, result.size());
+
+ Time now = Time::Now();
+ TimeDelta one_day = TimeDelta::FromDays(1);
+
+ // Create one with a 0 time.
+ EXPECT_TRUE(AddTimestampedLogin(&db, "1", L"foo1", Time()));
+ // Create one for now and +/- 1 day.
+ EXPECT_TRUE(AddTimestampedLogin(&db, "2", L"foo2", now - one_day));
+ EXPECT_TRUE(AddTimestampedLogin(&db, "3", L"foo3", now));
+ EXPECT_TRUE(AddTimestampedLogin(&db, "4", L"foo4", now + one_day));
+
+ // Verify inserts worked.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(4, result.size());
+ ClearResults(&result);
+
+ // Delete everything from today's date and on.
+ db.RemoveLoginsCreatedBetween(now, Time());
+
+ // Should have deleted half of what we inserted.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(2, result.size());
+ ClearResults(&result);
+
+ // Delete with 0 date (should delete all).
+ db.RemoveLoginsCreatedBetween(Time(), Time());
+
+ // Verify nothing is left.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(0, result.size());
+}
+
+TEST_F(WebDatabaseTest, BlacklistedLogins) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+ std::vector<PasswordForm*> result;
+
+ // Verify the database is empty.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ ASSERT_EQ(0, result.size());
+
+ // Save a form as blacklisted.
+ PasswordForm form;
+ form.origin = GURL(L"http://www.google.com/accounts/LoginAuth");
+ form.action = GURL(L"http://www.google.com/accounts/Login");
+ form.username_element = L"Email";
+ form.password_element = L"Passwd";
+ form.submit_element = L"signIn";
+ form.signon_realm = "http://www.google.com";
+ form.ssl_valid = false;
+ form.preferred = true;
+ form.blacklisted_by_user = true;
+ form.scheme = PasswordForm::SCHEME_HTML;
+ EXPECT_TRUE(db.AddLogin(form));
+
+ // Get all non-blacklisted logins (should be none).
+ EXPECT_TRUE(db.GetAllLogins(&result, false));
+ ASSERT_EQ(0, result.size());
+
+ // GetLogins should give the blacklisted result.
+ EXPECT_TRUE(db.GetLogins(form, &result));
+ EXPECT_EQ(1, result.size());
+ ClearResults(&result);
+
+ // So should GetAll including blacklisted.
+ EXPECT_TRUE(db.GetAllLogins(&result, true));
+ EXPECT_EQ(1, result.size());
+ ClearResults(&result);
+}
+
+TEST_F(WebDatabaseTest, WebAppHasAllImages) {
+ WebDatabase db;
+
+ EXPECT_TRUE(db.Init(file_));
+ GURL url("http://google.com");
+
+ // Initial value for unknown web app should be false.
+ EXPECT_FALSE(db.GetWebAppHasAllImages(url));
+
+ // Set the value and make sure it took.
+ EXPECT_TRUE(db.SetWebAppHasAllImages(url, true));
+ EXPECT_TRUE(db.GetWebAppHasAllImages(url));
+
+ // Remove the app and make sure value reverts to default.
+ EXPECT_TRUE(db.RemoveWebApp(url));
+ EXPECT_FALSE(db.GetWebAppHasAllImages(url));
+}
+
+TEST_F(WebDatabaseTest, WebAppImages) {
+ WebDatabase db;
+
+ ASSERT_TRUE(db.Init(file_));
+ GURL url("http://google.com");
+
+ // Web app should initially have no images.
+ std::vector<SkBitmap> images;
+ ASSERT_TRUE(db.GetWebAppImages(url, &images));
+ ASSERT_EQ(0, images.size());
+
+ // Add an image.
+ SkBitmap image;
+ image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ image.allocPixels();
+ image.eraseColor(SK_ColorBLACK);
+ ASSERT_TRUE(db.SetWebAppImage(url, image));
+
+ // Make sure we get the image back.
+ ASSERT_TRUE(db.GetWebAppImages(url, &images));
+ ASSERT_EQ(1, images.size());
+ ASSERT_EQ(16, images[0].width());
+ ASSERT_EQ(16, images[0].height());
+
+ // Add another 16x16 image and make sure it replaces the original.
+ image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ image.allocPixels();
+ image.eraseColor(SK_ColorBLACK);
+ // Some random pixels so that we can identify the image.
+ *(reinterpret_cast<unsigned char*>(image.getPixels())) = 0xAB;
+ ASSERT_TRUE(db.SetWebAppImage(url, image));
+ images.clear();
+ ASSERT_TRUE(db.GetWebAppImages(url, &images));
+ ASSERT_EQ(1, images.size());
+ ASSERT_EQ(16, images[0].width());
+ ASSERT_EQ(16, images[0].height());
+ images[0].lockPixels();
+ unsigned char* pixels =
+ reinterpret_cast<unsigned char*>(images[0].getPixels());
+ ASSERT_TRUE(pixels != NULL);
+ ASSERT_EQ(0xAB, *pixels);
+ images[0].unlockPixels();
+
+ // Add another image at a bigger size.
+ image.setConfig(SkBitmap::kARGB_8888_Config, 32, 32);
+ image.allocPixels();
+ image.eraseColor(SK_ColorBLACK);
+ ASSERT_TRUE(db.SetWebAppImage(url, image));
+
+ // Make sure we get both images back.
+ images.clear();
+ ASSERT_TRUE(db.GetWebAppImages(url, &images));
+ ASSERT_EQ(2, images.size());
+ if (images[0].width() == 16) {
+ ASSERT_EQ(16, images[0].width());
+ ASSERT_EQ(16, images[0].height());
+ ASSERT_EQ(32, images[1].width());
+ ASSERT_EQ(32, images[1].height());
+ } else {
+ ASSERT_EQ(32, images[0].width());
+ ASSERT_EQ(32, images[0].height());
+ ASSERT_EQ(16, images[1].width());
+ ASSERT_EQ(16, images[1].height());
+ }
+}