1// Copyright (c) 2011 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 "chrome/browser/browsing_data_indexed_db_helper.h"
6
7#include "base/file_util.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop.h"
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/profiles/profile.h"
13#include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h"
14#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
15#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
16#include "content/browser/browser_thread.h"
17#include "content/browser/in_process_webkit/webkit_context.h"
18#include "webkit/glue/webkit_glue.h"
19
20using WebKit::WebSecurityOrigin;
21
22namespace {
23
24class BrowsingDataIndexedDBHelperImpl : public BrowsingDataIndexedDBHelper {
25 public:
26  explicit BrowsingDataIndexedDBHelperImpl(Profile* profile);
27
28  virtual void StartFetching(
29      Callback1<const std::vector<IndexedDBInfo>& >::Type* callback);
30  virtual void CancelNotification();
31  virtual void DeleteIndexedDBFile(const FilePath& file_path);
32
33 private:
34  virtual ~BrowsingDataIndexedDBHelperImpl();
35
36  // Enumerates all indexed database files in the WEBKIT thread.
37  void FetchIndexedDBInfoInWebKitThread();
38  // Notifies the completion callback in the UI thread.
39  void NotifyInUIThread();
40  // Delete a single indexed database file in the WEBKIT thread.
41  void DeleteIndexedDBFileInWebKitThread(const FilePath& file_path);
42
43  Profile* profile_;
44
45  // This only mutates in the WEBKIT thread.
46  std::vector<IndexedDBInfo> indexed_db_info_;
47
48  // This only mutates on the UI thread.
49  scoped_ptr<Callback1<const std::vector<IndexedDBInfo>& >::Type >
50      completion_callback_;
51  // Indicates whether or not we're currently fetching information:
52  // it's true when StartFetching() is called in the UI thread, and it's reset
53  // after we notified the callback in the UI thread.
54  // This only mutates on the UI thread.
55  bool is_fetching_;
56
57  DISALLOW_COPY_AND_ASSIGN(BrowsingDataIndexedDBHelperImpl);
58};
59
60BrowsingDataIndexedDBHelperImpl::BrowsingDataIndexedDBHelperImpl(
61    Profile* profile)
62    : profile_(profile),
63      completion_callback_(NULL),
64      is_fetching_(false) {
65  DCHECK(profile_);
66}
67
68BrowsingDataIndexedDBHelperImpl::~BrowsingDataIndexedDBHelperImpl() {
69}
70
71void BrowsingDataIndexedDBHelperImpl::StartFetching(
72    Callback1<const std::vector<IndexedDBInfo>& >::Type* callback) {
73  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74  DCHECK(!is_fetching_);
75  DCHECK(callback);
76  is_fetching_ = true;
77  completion_callback_.reset(callback);
78  BrowserThread::PostTask(
79      BrowserThread::WEBKIT, FROM_HERE,
80      NewRunnableMethod(
81          this,
82          &BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread));
83}
84
85void BrowsingDataIndexedDBHelperImpl::CancelNotification() {
86  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87  completion_callback_.reset(NULL);
88}
89
90void BrowsingDataIndexedDBHelperImpl::DeleteIndexedDBFile(
91    const FilePath& file_path) {
92  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
93  BrowserThread::PostTask(
94      BrowserThread::WEBKIT, FROM_HERE,
95       NewRunnableMethod(
96           this,
97           &BrowsingDataIndexedDBHelperImpl::
98              DeleteIndexedDBFileInWebKitThread,
99           file_path));
100}
101
102void BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread() {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
104  file_util::FileEnumerator file_enumerator(
105      profile_->GetWebKitContext()->data_path().Append(
106          IndexedDBContext::kIndexedDBDirectory),
107      false, file_util::FileEnumerator::FILES);
108  for (FilePath file_path = file_enumerator.Next(); !file_path.empty();
109       file_path = file_enumerator.Next()) {
110    if (file_path.Extension() == IndexedDBContext::kIndexedDBExtension) {
111      WebSecurityOrigin web_security_origin =
112          WebSecurityOrigin::createFromDatabaseIdentifier(
113              webkit_glue::FilePathToWebString(file_path.BaseName()));
114      if (EqualsASCII(web_security_origin.protocol(),
115                      chrome::kExtensionScheme)) {
116        // Extension state is not considered browsing data.
117        continue;
118      }
119      base::PlatformFileInfo file_info;
120      bool ret = file_util::GetFileInfo(file_path, &file_info);
121      if (ret) {
122        indexed_db_info_.push_back(IndexedDBInfo(
123            web_security_origin.protocol().utf8(),
124            web_security_origin.host().utf8(),
125            web_security_origin.port(),
126            web_security_origin.databaseIdentifier().utf8(),
127            web_security_origin.toString().utf8(),
128            file_path,
129            file_info.size,
130            file_info.last_modified));
131      }
132    }
133  }
134
135  BrowserThread::PostTask(
136      BrowserThread::UI, FROM_HERE,
137      NewRunnableMethod(
138          this, &BrowsingDataIndexedDBHelperImpl::NotifyInUIThread));
139}
140
141void BrowsingDataIndexedDBHelperImpl::NotifyInUIThread() {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
143  DCHECK(is_fetching_);
144  // Note: completion_callback_ mutates only in the UI thread, so it's safe to
145  // test it here.
146  if (completion_callback_ != NULL) {
147    completion_callback_->Run(indexed_db_info_);
148    completion_callback_.reset();
149  }
150  is_fetching_ = false;
151}
152
153void BrowsingDataIndexedDBHelperImpl::DeleteIndexedDBFileInWebKitThread(
154    const FilePath& file_path) {
155  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
156  // TODO(jochen): implement this once it's possible to delete indexed DBs.
157}
158
159}  // namespace
160
161BrowsingDataIndexedDBHelper::IndexedDBInfo::IndexedDBInfo(
162    const std::string& protocol,
163    const std::string& host,
164    unsigned short port,
165    const std::string& database_identifier,
166    const std::string& origin,
167    const FilePath& file_path,
168    int64 size,
169    base::Time last_modified)
170    : protocol(protocol),
171      host(host),
172      port(port),
173      database_identifier(database_identifier),
174      origin(origin),
175      file_path(file_path),
176      size(size),
177      last_modified(last_modified) {
178}
179
180BrowsingDataIndexedDBHelper::IndexedDBInfo::~IndexedDBInfo() {}
181
182// static
183BrowsingDataIndexedDBHelper* BrowsingDataIndexedDBHelper::Create(
184    Profile* profile) {
185  return new BrowsingDataIndexedDBHelperImpl(profile);
186}
187
188CannedBrowsingDataIndexedDBHelper::
189PendingIndexedDBInfo::PendingIndexedDBInfo() {
190}
191
192CannedBrowsingDataIndexedDBHelper::
193PendingIndexedDBInfo::PendingIndexedDBInfo(const GURL& origin,
194                                           const string16& description)
195    : origin(origin),
196      description(description) {
197}
198
199CannedBrowsingDataIndexedDBHelper::
200PendingIndexedDBInfo::~PendingIndexedDBInfo() {
201}
202
203CannedBrowsingDataIndexedDBHelper::CannedBrowsingDataIndexedDBHelper(
204    Profile* profile)
205    : profile_(profile),
206      completion_callback_(NULL),
207      is_fetching_(false) {
208  DCHECK(profile);
209}
210
211CannedBrowsingDataIndexedDBHelper* CannedBrowsingDataIndexedDBHelper::Clone() {
212  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213  CannedBrowsingDataIndexedDBHelper* clone =
214      new CannedBrowsingDataIndexedDBHelper(profile_);
215
216  base::AutoLock auto_lock(lock_);
217  clone->pending_indexed_db_info_ = pending_indexed_db_info_;
218  clone->indexed_db_info_ = indexed_db_info_;
219  return clone;
220}
221
222void CannedBrowsingDataIndexedDBHelper::AddIndexedDB(
223    const GURL& origin, const string16& description) {
224  base::AutoLock auto_lock(lock_);
225  pending_indexed_db_info_.push_back(PendingIndexedDBInfo(origin, description));
226}
227
228void CannedBrowsingDataIndexedDBHelper::Reset() {
229  base::AutoLock auto_lock(lock_);
230  indexed_db_info_.clear();
231  pending_indexed_db_info_.clear();
232}
233
234bool CannedBrowsingDataIndexedDBHelper::empty() const {
235  base::AutoLock auto_lock(lock_);
236  return indexed_db_info_.empty() && pending_indexed_db_info_.empty();
237}
238
239void CannedBrowsingDataIndexedDBHelper::StartFetching(
240    Callback1<const std::vector<IndexedDBInfo>& >::Type* callback) {
241  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242  DCHECK(!is_fetching_);
243  DCHECK(callback);
244  is_fetching_ = true;
245  completion_callback_.reset(callback);
246  BrowserThread::PostTask(BrowserThread::WEBKIT, FROM_HERE, NewRunnableMethod(
247      this,
248      &CannedBrowsingDataIndexedDBHelper::ConvertPendingInfoInWebKitThread));
249}
250
251CannedBrowsingDataIndexedDBHelper::~CannedBrowsingDataIndexedDBHelper() {}
252
253void CannedBrowsingDataIndexedDBHelper::ConvertPendingInfoInWebKitThread() {
254  base::AutoLock auto_lock(lock_);
255  for (std::vector<PendingIndexedDBInfo>::const_iterator
256       info = pending_indexed_db_info_.begin();
257       info != pending_indexed_db_info_.end(); ++info) {
258    WebSecurityOrigin web_security_origin =
259        WebSecurityOrigin::createFromString(
260            UTF8ToUTF16(info->origin.spec()));
261    std::string security_origin(web_security_origin.toString().utf8());
262
263    bool duplicate = false;
264    for (std::vector<IndexedDBInfo>::iterator
265         indexed_db = indexed_db_info_.begin();
266         indexed_db != indexed_db_info_.end(); ++indexed_db) {
267      if (indexed_db->origin == security_origin) {
268        duplicate = true;
269        break;
270      }
271    }
272    if (duplicate)
273      continue;
274
275    indexed_db_info_.push_back(IndexedDBInfo(
276        web_security_origin.protocol().utf8(),
277        web_security_origin.host().utf8(),
278        web_security_origin.port(),
279        web_security_origin.databaseIdentifier().utf8(),
280        security_origin,
281        profile_->GetWebKitContext()->indexed_db_context()->
282            GetIndexedDBFilePath(web_security_origin.databaseIdentifier()),
283        0,
284        base::Time()));
285  }
286  pending_indexed_db_info_.clear();
287
288  BrowserThread::PostTask(
289      BrowserThread::UI, FROM_HERE,
290      NewRunnableMethod(
291          this, &CannedBrowsingDataIndexedDBHelper::NotifyInUIThread));
292}
293
294void CannedBrowsingDataIndexedDBHelper::NotifyInUIThread() {
295  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296  DCHECK(is_fetching_);
297  // Note: completion_callback_ mutates only in the UI thread, so it's safe to
298  // test it here.
299  if (completion_callback_ != NULL) {
300    completion_callback_->Run(indexed_db_info_);
301    completion_callback_.reset();
302  }
303  is_fetching_ = false;
304}
305