Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_internals_ui.cc |
| diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc |
| index 1869091ab5de94fd822089d65823288aeac7df69..191a0607a7f31afad5c6b4d281ca934257ecaa8e 100644 |
| --- a/content/browser/indexed_db/indexed_db_internals_ui.cc |
| +++ b/content/browser/indexed_db/indexed_db_internals_ui.cc |
| @@ -5,19 +5,30 @@ |
| #include "content/browser/indexed_db/indexed_db_internals_ui.h" |
| #include <algorithm> |
| +#include <string> |
| #include "base/bind.h" |
| +#include "base/files/scoped_temp_dir.h" |
| #include "base/memory/scoped_vector.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/values.h" |
| +#include "components/zip/zip.h" |
| +#include "content/browser/indexed_db/indexed_db_context_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/download_manager.h" |
| +#include "content/public/browser/download_url_parameters.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "content/public/common/url_constants.h" |
| #include "grit/content_resources.h" |
| +#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" |
| +#include "webkit/base/file_path_string_conversions.h" |
| +#include "webkit/database/database_util.h" |
| + |
| +using webkit_database::DatabaseUtil; |
| namespace content { |
| @@ -27,6 +38,11 @@ IndexedDBInternalsUI::IndexedDBInternalsUI(WebUI* web_ui) |
| "getAllOrigins", |
| base::Bind(&IndexedDBInternalsUI::GetAllOrigins, base::Unretained(this))); |
| + web_ui->RegisterMessageCallback( |
| + "downloadOriginData", |
| + base::Bind(&IndexedDBInternalsUI::DownloadOriginData, |
| + base::Unretained(this))); |
| + |
| WebUIDataSource* source = |
| WebUIDataSource::Create(kChromeUIIndexedDBInternalsHost); |
| source->SetUseJsonJSFormatV2(); |
| @@ -80,8 +96,8 @@ bool HostNameComparator(const IndexedDBInfo& i, const IndexedDBInfo& j) { |
| } |
| void IndexedDBInternalsUI::GetAllOriginsOnWebkitThread( |
| - scoped_ptr<ContextList> contexts, |
| - scoped_ptr<std::vector<base::FilePath> > context_paths) { |
| + const scoped_ptr<ContextList> contexts, |
| + const scoped_ptr<std::vector<base::FilePath> > context_paths) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); |
| DCHECK_EQ(contexts->size(), context_paths->size()); |
| @@ -124,4 +140,178 @@ void IndexedDBInternalsUI::OnOriginsReady( |
| "indexeddb.onOriginsReady", urls, base::StringValue(path.value())); |
| } |
| +static void FindContext(const base::FilePath& partition_path, |
| + StoragePartition** result_partition, |
| + scoped_refptr<IndexedDBContextImpl>* result_context, |
| + StoragePartition* storage_partition) { |
| + if (storage_partition->GetPath() == partition_path) { |
| + *result_partition = storage_partition; |
| + *result_context = static_cast<IndexedDBContextImpl*>( |
| + storage_partition->GetIndexedDBContext()); |
| + } |
| +} |
| + |
| +void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + base::FilePath::StringType path_string; |
| + if (!args->GetString(0, &path_string)) |
| + return; |
| + const base::FilePath partition_path(path_string); |
| + |
| + std::string url_string; |
| + if (!args->GetString(1, &url_string)) |
| + return; |
| + const GURL origin_url(url_string); |
| + |
| + // search the origins to find the right context |
| + |
| + BrowserContext* browser_context = |
| + web_ui()->GetWebContents()->GetBrowserContext(); |
| + |
| + scoped_refptr<IndexedDBContextImpl> result_context; |
| + StoragePartition* result_partition; |
| + scoped_ptr<ContextList> contexts(new ContextList); |
| + BrowserContext::StoragePartitionCallback cb = base::Bind( |
| + &FindContext, partition_path, &result_partition, &result_context); |
| + BrowserContext::ForEachStoragePartition(browser_context, cb); |
| + DCHECK(result_partition); |
| + DCHECK(result_context); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::WEBKIT_DEPRECATED, |
| + FROM_HERE, |
| + base::Bind(&IndexedDBInternalsUI::DownloadOriginDataOnWebkitThread, |
| + base::Unretained(this), |
| + result_partition->GetPath(), |
| + result_context, |
| + origin_url)); |
| +} |
| + |
| +void IndexedDBInternalsUI::DownloadOriginDataOnWebkitThread( |
| + const base::FilePath& partition_path, |
| + const scoped_refptr<IndexedDBContextImpl> context, |
| + const GURL& origin_url) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); |
| + |
| + if (!context->HasOrigin(origin_url)) |
| + return; |
| + |
| + context->ForceClose(origin_url); |
| + |
| + base::ScopedTempDir temp_dir; |
| + if (!temp_dir.CreateUniqueTempDir()) |
| + return; |
| + |
| + // This will get cleaned up on the File thread after the download |
| + // has completed. |
| + base::FilePath temp_path = temp_dir.Take(); |
| + |
| + base::string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url); |
| + base::FilePath::StringType zip_name = |
| + webkit_base::WebStringToFilePathString(origin_id); |
| + base::FilePath zip_path = temp_path.Append(zip_name).AddExtension("zip"); |
| + |
| + // This happens on the "webkit" thread (which is really just the IndexedDB |
| + // thread) as a simple way to avoid another script reopening the origin |
| + // while we are zipping. |
| + zip::Zip(context->GetFilePath(origin_url), zip_path, true); |
| + |
| + BrowserThread::PostTask(BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&IndexedDBInternalsUI::OnDownloadDataReady, |
| + base::Unretained(this), |
| + partition_path, |
| + origin_url, |
| + temp_path, |
| + zip_path)); |
| + |
| + LOG(ERROR) << "Origin data ready in " << zip_path.value(); |
|
jsbell
2013/04/18 22:08:52
Don't forget to remove this.
|
| +} |
| + |
| +void IndexedDBInternalsUI::OnDownloadDataReady( |
| + const base::FilePath& partition_path, |
| + const GURL& origin_url, |
| + const base::FilePath temp_path, |
| + const base::FilePath zip_path) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + const GURL url = GURL("file://" + zip_path.value()); |
| + BrowserContext* browser_context = |
| + web_ui()->GetWebContents()->GetBrowserContext(); |
| + scoped_ptr<DownloadUrlParameters> dl_params( |
| + DownloadUrlParameters::FromWebContents(web_ui()->GetWebContents(), url)); |
| + DownloadManager* dlm = BrowserContext::GetDownloadManager(browser_context); |
| + |
| + const GURL referrer(web_ui()->GetWebContents()->GetURL()); |
| + dl_params->set_referrer( |
| + content::Referrer(referrer, WebKit::WebReferrerPolicyDefault)); |
| + |
| + // This is how to watch for the download to finish: first wait for it |
| + // to start, then attach a DownloadItem::Observer to observe the |
| + // state change to the finished state. |
| + dl_params->set_callback(base::Bind(&IndexedDBInternalsUI::OnDownloadStarted, |
| + base::Unretained(this), |
| + partition_path, |
| + origin_url, |
| + temp_path)); |
| + dlm->DownloadUrl(dl_params.Pass()); |
| + |
| + LOG(ERROR) << "I'd start to download: " << url.spec(); |
|
jsbell
2013/04/18 22:08:52
Don't forget to remove this.
|
| +} |
| + |
| +// The entire purpose of this class is to delete the temp file after |
| +// the download is complete. |
| +class FileDeleter : public DownloadItem::Observer { |
| + public: |
| + explicit FileDeleter(const base::FilePath& temp_dir) : temp_dir_(temp_dir) {} |
| + virtual ~FileDeleter(); |
| + |
| + virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE; |
| + virtual void OnDownloadOpened(DownloadItem* item) OVERRIDE {} |
| + virtual void OnDownloadRemoved(DownloadItem* item) OVERRIDE {} |
| + virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE {} |
| + |
| + private: |
| + const base::FilePath temp_dir_; |
| +}; |
| + |
| +void FileDeleter::OnDownloadUpdated(DownloadItem* item) { |
| + switch (item->GetState()) { |
| + case DownloadItem::IN_PROGRESS: |
| + break; |
| + case DownloadItem::COMPLETE: |
| + case DownloadItem::CANCELLED: |
| + case DownloadItem::INTERRUPTED: { |
| + item->RemoveObserver(this); |
| + BrowserThread::DeleteOnFileThread::Destruct(this); |
| + } break; |
|
jsbell
2013/04/18 22:08:52
Put break; inside braces?
|
| + default: |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +FileDeleter::~FileDeleter() { |
| + base::ScopedTempDir path; |
| + (void) path.Set(temp_dir_); |
| +} |
| + |
| +void IndexedDBInternalsUI::OnDownloadStarted( |
| + const base::FilePath& partition_path, |
| + const GURL& origin_url, |
| + const base::FilePath& temp_path, |
| + DownloadItem* item, |
| + net::Error error) { |
| + |
| + if (error != net::OK) { |
| + LOG(ERROR) |
| + << "Error downloading database dump: " << net::ErrorToString(error); |
|
jsbell
2013/04/18 22:08:52
Nit: Put first bit of log on previous line. (I thi
|
| + return; |
| + } |
| + |
| + item->AddObserver(new FileDeleter(temp_path)); |
| + web_ui()->CallJavascriptFunction("indexeddb.onOriginDownloadReady", |
| + base::StringValue(partition_path.value()), |
| + base::StringValue(origin_url.spec())); |
| +} |
| + |
| } // namespace content |