1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/webdata/common/web_database_service.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "components/webdata/common/web_data_request_manager.h"
10#include "components/webdata/common/web_data_results.h"
11#include "components/webdata/common/web_data_service_backend.h"
12#include "components/webdata/common/web_data_service_consumer.h"
13
14using base::Bind;
15using base::FilePath;
16
17// Receives messages from the backend on the DB thread, posts them to
18// WebDatabaseService on the UI thread.
19class WebDatabaseService::BackendDelegate :
20    public WebDataServiceBackend::Delegate {
21 public:
22  BackendDelegate(
23      const base::WeakPtr<WebDatabaseService>& web_database_service)
24      : web_database_service_(web_database_service),
25        callback_thread_(base::MessageLoopProxy::current()) {
26  }
27
28  virtual void DBLoaded(sql::InitStatus status) OVERRIDE {
29    callback_thread_->PostTask(
30        FROM_HERE,
31        base::Bind(&WebDatabaseService::OnDatabaseLoadDone,
32                   web_database_service_,
33                   status));
34  }
35 private:
36  const base::WeakPtr<WebDatabaseService> web_database_service_;
37  scoped_refptr<base::MessageLoopProxy> callback_thread_;
38};
39
40WebDatabaseService::WebDatabaseService(
41    const base::FilePath& path,
42    const scoped_refptr<base::MessageLoopProxy>& ui_thread,
43    const scoped_refptr<base::MessageLoopProxy>& db_thread)
44    : base::RefCountedDeleteOnMessageLoop<WebDatabaseService>(ui_thread),
45      path_(path),
46      weak_ptr_factory_(this),
47      db_loaded_(false),
48      db_thread_(db_thread) {
49  // WebDatabaseService should be instantiated on UI thread.
50  DCHECK(ui_thread->BelongsToCurrentThread());
51  // WebDatabaseService requires DB thread if instantiated.
52  DCHECK(db_thread.get());
53}
54
55WebDatabaseService::~WebDatabaseService() {
56}
57
58void WebDatabaseService::AddTable(scoped_ptr<WebDatabaseTable> table) {
59  if (!wds_backend_.get()) {
60    wds_backend_ = new WebDataServiceBackend(
61        path_, new BackendDelegate(weak_ptr_factory_.GetWeakPtr()),
62        db_thread_);
63  }
64  wds_backend_->AddTable(table.Pass());
65}
66
67void WebDatabaseService::LoadDatabase() {
68  DCHECK(wds_backend_.get());
69
70  db_thread_->PostTask(
71      FROM_HERE,
72      Bind(&WebDataServiceBackend::InitDatabase, wds_backend_));
73}
74
75void WebDatabaseService::UnloadDatabase() {
76  db_loaded_ = false;
77  if (!wds_backend_.get())
78    return;
79  db_thread_->PostTask(FROM_HERE,
80      Bind(&WebDataServiceBackend::ShutdownDatabase,
81           wds_backend_, true));
82}
83
84void WebDatabaseService::ShutdownDatabase() {
85  db_loaded_ = false;
86  weak_ptr_factory_.InvalidateWeakPtrs();
87  loaded_callbacks_.clear();
88  error_callbacks_.clear();
89  if (!wds_backend_.get())
90    return;
91  db_thread_->PostTask(FROM_HERE,
92      Bind(&WebDataServiceBackend::ShutdownDatabase,
93           wds_backend_, false));
94}
95
96WebDatabase* WebDatabaseService::GetDatabaseOnDB() const {
97  DCHECK(db_thread_->BelongsToCurrentThread());
98  if (!wds_backend_.get())
99    return NULL;
100  return wds_backend_->database();
101}
102
103scoped_refptr<WebDataServiceBackend> WebDatabaseService::GetBackend() const {
104  return wds_backend_;
105}
106
107void WebDatabaseService::ScheduleDBTask(
108    const tracked_objects::Location& from_here,
109    const WriteTask& task) {
110  if (!wds_backend_.get()) {
111    NOTREACHED() << "Task scheduled after Shutdown()";
112    return;
113  }
114
115  scoped_ptr<WebDataRequest> request(
116      new WebDataRequest(NULL, wds_backend_->request_manager().get()));
117
118  db_thread_->PostTask(from_here,
119      Bind(&WebDataServiceBackend::DBWriteTaskWrapper, wds_backend_,
120           task, base::Passed(&request)));
121}
122
123WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
124    const tracked_objects::Location& from_here,
125    const ReadTask& task,
126    WebDataServiceConsumer* consumer) {
127  DCHECK(consumer);
128  WebDataServiceBase::Handle handle = 0;
129
130  if (!wds_backend_.get()) {
131    NOTREACHED() << "Task scheduled after Shutdown()";
132    return handle;
133  }
134
135  scoped_ptr<WebDataRequest> request(
136      new WebDataRequest(consumer, wds_backend_->request_manager().get()));
137  handle = request->GetHandle();
138
139  db_thread_->PostTask(from_here,
140      Bind(&WebDataServiceBackend::DBReadTaskWrapper, wds_backend_,
141           task, base::Passed(&request)));
142
143  return handle;
144}
145
146void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) {
147  if (!wds_backend_.get())
148    return;
149  wds_backend_->request_manager()->CancelRequest(h);
150}
151
152void WebDatabaseService::RegisterDBLoadedCallback(
153    const DBLoadedCallback& callback) {
154  loaded_callbacks_.push_back(callback);
155}
156
157void WebDatabaseService::RegisterDBErrorCallback(
158    const DBLoadErrorCallback& callback) {
159  error_callbacks_.push_back(callback);
160}
161
162void WebDatabaseService::OnDatabaseLoadDone(sql::InitStatus status) {
163  if (status == sql::INIT_OK) {
164    db_loaded_ = true;
165
166    for (size_t i = 0; i < loaded_callbacks_.size(); i++) {
167      if (!loaded_callbacks_[i].is_null())
168        loaded_callbacks_[i].Run();
169    }
170
171    loaded_callbacks_.clear();
172  } else {
173    // Notify that the database load failed.
174    for (size_t i = 0; i < error_callbacks_.size(); i++) {
175      if (!error_callbacks_[i].is_null())
176        error_callbacks_[i].Run(status);
177    }
178
179    error_callbacks_.clear();
180  }
181}
182