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
20 using WebKit::WebSecurityOrigin;
21
22 namespace {
23
24 class 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
BrowsingDataIndexedDBHelperImpl(Profile * profile)60 BrowsingDataIndexedDBHelperImpl::BrowsingDataIndexedDBHelperImpl(
61 Profile* profile)
62 : profile_(profile),
63 completion_callback_(NULL),
64 is_fetching_(false) {
65 DCHECK(profile_);
66 }
67
~BrowsingDataIndexedDBHelperImpl()68 BrowsingDataIndexedDBHelperImpl::~BrowsingDataIndexedDBHelperImpl() {
69 }
70
StartFetching(Callback1<const std::vector<IndexedDBInfo> &>::Type * callback)71 void 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
CancelNotification()85 void BrowsingDataIndexedDBHelperImpl::CancelNotification() {
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87 completion_callback_.reset(NULL);
88 }
89
DeleteIndexedDBFile(const FilePath & file_path)90 void 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
FetchIndexedDBInfoInWebKitThread()102 void 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
NotifyInUIThread()141 void 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
DeleteIndexedDBFileInWebKitThread(const FilePath & file_path)153 void 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
IndexedDBInfo(const std::string & protocol,const std::string & host,unsigned short port,const std::string & database_identifier,const std::string & origin,const FilePath & file_path,int64 size,base::Time last_modified)161 BrowsingDataIndexedDBHelper::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
~IndexedDBInfo()180 BrowsingDataIndexedDBHelper::IndexedDBInfo::~IndexedDBInfo() {}
181
182 // static
Create(Profile * profile)183 BrowsingDataIndexedDBHelper* BrowsingDataIndexedDBHelper::Create(
184 Profile* profile) {
185 return new BrowsingDataIndexedDBHelperImpl(profile);
186 }
187
188 CannedBrowsingDataIndexedDBHelper::
PendingIndexedDBInfo()189 PendingIndexedDBInfo::PendingIndexedDBInfo() {
190 }
191
192 CannedBrowsingDataIndexedDBHelper::
PendingIndexedDBInfo(const GURL & origin,const string16 & description)193 PendingIndexedDBInfo::PendingIndexedDBInfo(const GURL& origin,
194 const string16& description)
195 : origin(origin),
196 description(description) {
197 }
198
199 CannedBrowsingDataIndexedDBHelper::
~PendingIndexedDBInfo()200 PendingIndexedDBInfo::~PendingIndexedDBInfo() {
201 }
202
CannedBrowsingDataIndexedDBHelper(Profile * profile)203 CannedBrowsingDataIndexedDBHelper::CannedBrowsingDataIndexedDBHelper(
204 Profile* profile)
205 : profile_(profile),
206 completion_callback_(NULL),
207 is_fetching_(false) {
208 DCHECK(profile);
209 }
210
Clone()211 CannedBrowsingDataIndexedDBHelper* 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
AddIndexedDB(const GURL & origin,const string16 & description)222 void 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
Reset()228 void CannedBrowsingDataIndexedDBHelper::Reset() {
229 base::AutoLock auto_lock(lock_);
230 indexed_db_info_.clear();
231 pending_indexed_db_info_.clear();
232 }
233
empty() const234 bool CannedBrowsingDataIndexedDBHelper::empty() const {
235 base::AutoLock auto_lock(lock_);
236 return indexed_db_info_.empty() && pending_indexed_db_info_.empty();
237 }
238
StartFetching(Callback1<const std::vector<IndexedDBInfo> &>::Type * callback)239 void 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
~CannedBrowsingDataIndexedDBHelper()251 CannedBrowsingDataIndexedDBHelper::~CannedBrowsingDataIndexedDBHelper() {}
252
ConvertPendingInfoInWebKitThread()253 void 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
NotifyInUIThread()294 void 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