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 60 BrowsingDataIndexedDBHelperImpl::BrowsingDataIndexedDBHelperImpl( 61 Profile* profile) 62 : profile_(profile), 63 completion_callback_(NULL), 64 is_fetching_(false) { 65 DCHECK(profile_); 66 } 67 68 BrowsingDataIndexedDBHelperImpl::~BrowsingDataIndexedDBHelperImpl() { 69 } 70 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 85 void BrowsingDataIndexedDBHelperImpl::CancelNotification() { 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 87 completion_callback_.reset(NULL); 88 } 89 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 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 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 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 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 180 BrowsingDataIndexedDBHelper::IndexedDBInfo::~IndexedDBInfo() {} 181 182 // static 183 BrowsingDataIndexedDBHelper* BrowsingDataIndexedDBHelper::Create( 184 Profile* profile) { 185 return new BrowsingDataIndexedDBHelperImpl(profile); 186 } 187 188 CannedBrowsingDataIndexedDBHelper:: 189 PendingIndexedDBInfo::PendingIndexedDBInfo() { 190 } 191 192 CannedBrowsingDataIndexedDBHelper:: 193 PendingIndexedDBInfo::PendingIndexedDBInfo(const GURL& origin, 194 const string16& description) 195 : origin(origin), 196 description(description) { 197 } 198 199 CannedBrowsingDataIndexedDBHelper:: 200 PendingIndexedDBInfo::~PendingIndexedDBInfo() { 201 } 202 203 CannedBrowsingDataIndexedDBHelper::CannedBrowsingDataIndexedDBHelper( 204 Profile* profile) 205 : profile_(profile), 206 completion_callback_(NULL), 207 is_fetching_(false) { 208 DCHECK(profile); 209 } 210 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 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 228 void CannedBrowsingDataIndexedDBHelper::Reset() { 229 base::AutoLock auto_lock(lock_); 230 indexed_db_info_.clear(); 231 pending_indexed_db_info_.clear(); 232 } 233 234 bool CannedBrowsingDataIndexedDBHelper::empty() const { 235 base::AutoLock auto_lock(lock_); 236 return indexed_db_info_.empty() && pending_indexed_db_info_.empty(); 237 } 238 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 251 CannedBrowsingDataIndexedDBHelper::~CannedBrowsingDataIndexedDBHelper() {} 252 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 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