Home | History | Annotate | Download | only in browser
      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