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 5979b2cf507f6f8060106a0ba53942e2c05c9b48..2fc096bf921c8342c89d4149748cbc23726cb7d2 100644 |
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc |
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc |
@@ -5,19 +5,29 @@ |
#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 "base/memory/scoped_vector.h" |
+#include "components/zip/zip.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 +37,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(); |
@@ -124,4 +139,177 @@ void IndexedDBInternalsUI::OnOriginsReady( |
"indexeddb.onOriginsReady", urls, base::StringValue(path.value())); |
} |
+static void FindContext(const base::FilePath& partition_path, |
+ StoragePartition** result_partition, |
+ scoped_refptr<IndexedDBContext>* result_context, |
+ StoragePartition* storage_partition) { |
+ if (storage_partition->GetPath() == partition_path) { |
+ *result_partition = storage_partition; |
+ *result_context = 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<IndexedDBContext> 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, |
+ scoped_refptr<IndexedDBContext> 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(); |
+} |
+ |
+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(); |
+} |
+ |
+// 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; |
+ 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); |
+ return; |
+ } |
+ |
+ item->AddObserver(new FileDeleter(temp_path)); |
+ web_ui()->CallJavascriptFunction("indexeddb.onOriginDownloadReady", |
+ base::StringValue(partition_path.value()), |
+ base::StringValue(origin_url.spec())); |
+} |
+ |
} // namespace content |