- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_internals_ui.cc
1 // Copyright (c) 2013 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 "content/browser/indexed_db/indexed_db_internals_ui.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/threading/platform_thread.h"
12 #include "base/values.h"
13 #include "content/browser/indexed_db/indexed_db_context_impl.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/download_manager.h"
17 #include "content/public/browser/download_url_parameters.h"
18 #include "content/public/browser/storage_partition.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_ui.h"
21 #include "content/public/browser/web_ui_data_source.h"
22 #include "content/public/common/url_constants.h"
23 #include "grit/content_resources.h"
24 #include "third_party/zlib/google/zip.h"
25 #include "ui/base/text/bytes_formatting.h"
26 #include "webkit/common/database/database_identifier.h"
27
28 namespace content {
29
30 IndexedDBInternalsUI::IndexedDBInternalsUI(WebUI* web_ui)
31     : WebUIController(web_ui) {
32   web_ui->RegisterMessageCallback(
33       "getAllOrigins",
34       base::Bind(&IndexedDBInternalsUI::GetAllOrigins, base::Unretained(this)));
35
36   web_ui->RegisterMessageCallback(
37       "downloadOriginData",
38       base::Bind(&IndexedDBInternalsUI::DownloadOriginData,
39                  base::Unretained(this)));
40   web_ui->RegisterMessageCallback(
41       "forceClose",
42       base::Bind(&IndexedDBInternalsUI::ForceCloseOrigin,
43                  base::Unretained(this)));
44
45   WebUIDataSource* source =
46       WebUIDataSource::Create(kChromeUIIndexedDBInternalsHost);
47   source->SetUseJsonJSFormatV2();
48   source->SetJsonPath("strings.js");
49   source->AddResourcePath("indexeddb_internals.js",
50                           IDR_INDEXED_DB_INTERNALS_JS);
51   source->AddResourcePath("indexeddb_internals.css",
52                           IDR_INDEXED_DB_INTERNALS_CSS);
53   source->SetDefaultResource(IDR_INDEXED_DB_INTERNALS_HTML);
54
55   BrowserContext* browser_context =
56       web_ui->GetWebContents()->GetBrowserContext();
57   WebUIDataSource::Add(browser_context, source);
58 }
59
60 IndexedDBInternalsUI::~IndexedDBInternalsUI() {}
61
62 void IndexedDBInternalsUI::AddContextFromStoragePartition(
63     StoragePartition* partition) {
64   scoped_refptr<IndexedDBContext> context = partition->GetIndexedDBContext();
65   context->TaskRunner()->PostTask(
66       FROM_HERE,
67       base::Bind(&IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread,
68                  base::Unretained(this),
69                  context,
70                  partition->GetPath()));
71 }
72
73 void IndexedDBInternalsUI::GetAllOrigins(const base::ListValue* args) {
74   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75
76   BrowserContext* browser_context =
77       web_ui()->GetWebContents()->GetBrowserContext();
78
79   BrowserContext::StoragePartitionCallback cb =
80       base::Bind(&IndexedDBInternalsUI::AddContextFromStoragePartition,
81                  base::Unretained(this));
82   BrowserContext::ForEachStoragePartition(browser_context, cb);
83 }
84
85 void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
86     scoped_refptr<IndexedDBContext> context,
87     const base::FilePath& context_path) {
88   DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
89
90   scoped_ptr<ListValue> info_list(static_cast<IndexedDBContextImpl*>(
91       context.get())->GetAllOriginsDetails());
92
93   BrowserThread::PostTask(BrowserThread::UI,
94                           FROM_HERE,
95                           base::Bind(&IndexedDBInternalsUI::OnOriginsReady,
96                                      base::Unretained(this),
97                                      base::Passed(&info_list),
98                                      context_path));
99 }
100
101 void IndexedDBInternalsUI::OnOriginsReady(scoped_ptr<ListValue> origins,
102                                           const base::FilePath& path) {
103   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104   web_ui()->CallJavascriptFunction(
105       "indexeddb.onOriginsReady", *origins, base::StringValue(path.value()));
106 }
107
108 static void FindContext(const base::FilePath& partition_path,
109                         StoragePartition** result_partition,
110                         scoped_refptr<IndexedDBContextImpl>* result_context,
111                         StoragePartition* storage_partition) {
112   if (storage_partition->GetPath() == partition_path) {
113     *result_partition = storage_partition;
114     *result_context = static_cast<IndexedDBContextImpl*>(
115         storage_partition->GetIndexedDBContext());
116   }
117 }
118
119 bool IndexedDBInternalsUI::GetOriginData(
120     const base::ListValue* args,
121     base::FilePath* partition_path,
122     GURL* origin_url,
123     scoped_refptr<IndexedDBContextImpl>* context) {
124   base::FilePath::StringType path_string;
125   if (!args->GetString(0, &path_string))
126     return false;
127   *partition_path = base::FilePath(path_string);
128
129   std::string url_string;
130   if (!args->GetString(1, &url_string))
131     return false;
132
133   *origin_url = GURL(url_string);
134
135   return GetOriginContext(*partition_path, *origin_url, context);
136 }
137
138 bool IndexedDBInternalsUI::GetOriginContext(
139     const base::FilePath& path,
140     const GURL& origin_url,
141     scoped_refptr<IndexedDBContextImpl>* context) {
142   // search the origins to find the right context
143   BrowserContext* browser_context =
144       web_ui()->GetWebContents()->GetBrowserContext();
145
146   StoragePartition* result_partition;
147   BrowserContext::StoragePartitionCallback cb =
148       base::Bind(&FindContext, path, &result_partition, context);
149   BrowserContext::ForEachStoragePartition(browser_context, cb);
150
151   if (!result_partition || !(*context))
152     return false;
153
154   return true;
155 }
156
157 void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) {
158   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159
160   base::FilePath partition_path;
161   GURL origin_url;
162   scoped_refptr<IndexedDBContextImpl> context;
163   if (!GetOriginData(args, &partition_path, &origin_url, &context))
164     return;
165
166   DCHECK(context);
167   context->TaskRunner()->PostTask(
168       FROM_HERE,
169       base::Bind(&IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread,
170                  base::Unretained(this),
171                  partition_path,
172                  context,
173                  origin_url));
174 }
175
176 void IndexedDBInternalsUI::ForceCloseOrigin(const base::ListValue* args) {
177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
178
179   base::FilePath partition_path;
180   GURL origin_url;
181   scoped_refptr<IndexedDBContextImpl> context;
182   if (!GetOriginData(args, &partition_path, &origin_url, &context))
183     return;
184
185   context->TaskRunner()->PostTask(
186       FROM_HERE,
187       base::Bind(&IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread,
188                  base::Unretained(this),
189                  partition_path,
190                  context,
191                  origin_url));
192 }
193
194 void IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread(
195     const base::FilePath& partition_path,
196     const scoped_refptr<IndexedDBContextImpl> context,
197     const GURL& origin_url) {
198   DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
199
200   // Make sure the database hasn't been deleted since the page was loaded.
201   if (!context->IsInOriginSet(origin_url))
202     return;
203
204   context->ForceClose(origin_url);
205   size_t connection_count = context->GetConnectionCount(origin_url);
206
207   base::ScopedTempDir temp_dir;
208   if (!temp_dir.CreateUniqueTempDir())
209     return;
210
211   // This will get cleaned up on the File thread after the download
212   // has completed.
213   base::FilePath temp_path = temp_dir.Take();
214
215   std::string origin_id = webkit_database::GetIdentifierFromOrigin(origin_url);
216   base::FilePath zip_path =
217       temp_path.AppendASCII(origin_id).AddExtension(FILE_PATH_LITERAL("zip"));
218
219   // This happens on the "webkit" thread (which is really just the IndexedDB
220   // thread) as a simple way to avoid another script reopening the origin
221   // while we are zipping.
222   zip::Zip(context->GetFilePath(origin_url), zip_path, true);
223
224   BrowserThread::PostTask(BrowserThread::UI,
225                           FROM_HERE,
226                           base::Bind(&IndexedDBInternalsUI::OnDownloadDataReady,
227                                      base::Unretained(this),
228                                      partition_path,
229                                      origin_url,
230                                      temp_path,
231                                      zip_path,
232                                      connection_count));
233 }
234
235 void IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread(
236     const base::FilePath& partition_path,
237     const scoped_refptr<IndexedDBContextImpl> context,
238     const GURL& origin_url) {
239   DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
240
241   // Make sure the database hasn't been deleted since the page was loaded.
242   if (!context->IsInOriginSet(origin_url))
243     return;
244
245   context->ForceClose(origin_url);
246   size_t connection_count = context->GetConnectionCount(origin_url);
247
248   BrowserThread::PostTask(BrowserThread::UI,
249                           FROM_HERE,
250                           base::Bind(&IndexedDBInternalsUI::OnForcedClose,
251                                      base::Unretained(this),
252                                      partition_path,
253                                      origin_url,
254                                      connection_count));
255 }
256
257 void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path,
258                                          const GURL& origin_url,
259                                          size_t connection_count) {
260   web_ui()->CallJavascriptFunction(
261       "indexeddb.onForcedClose",
262       base::StringValue(partition_path.value()),
263       base::StringValue(origin_url.spec()),
264       base::FundamentalValue(double(connection_count)));
265 }
266
267 void IndexedDBInternalsUI::OnDownloadDataReady(
268     const base::FilePath& partition_path,
269     const GURL& origin_url,
270     const base::FilePath temp_path,
271     const base::FilePath zip_path,
272     size_t connection_count) {
273   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274   const GURL url = GURL(FILE_PATH_LITERAL("file://") + zip_path.value());
275   BrowserContext* browser_context =
276       web_ui()->GetWebContents()->GetBrowserContext();
277   scoped_ptr<DownloadUrlParameters> dl_params(
278       DownloadUrlParameters::FromWebContents(web_ui()->GetWebContents(), url));
279   DownloadManager* dlm = BrowserContext::GetDownloadManager(browser_context);
280
281   const GURL referrer(web_ui()->GetWebContents()->GetLastCommittedURL());
282   dl_params->set_referrer(
283       content::Referrer(referrer, WebKit::WebReferrerPolicyDefault));
284
285   // This is how to watch for the download to finish: first wait for it
286   // to start, then attach a DownloadItem::Observer to observe the
287   // state change to the finished state.
288   dl_params->set_callback(base::Bind(&IndexedDBInternalsUI::OnDownloadStarted,
289                                      base::Unretained(this),
290                                      partition_path,
291                                      origin_url,
292                                      temp_path,
293                                      connection_count));
294   dlm->DownloadUrl(dl_params.Pass());
295 }
296
297 // The entire purpose of this class is to delete the temp file after
298 // the download is complete.
299 class FileDeleter : public DownloadItem::Observer {
300  public:
301   explicit FileDeleter(const base::FilePath& temp_dir) : temp_dir_(temp_dir) {}
302   virtual ~FileDeleter();
303
304   virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE;
305   virtual void OnDownloadOpened(DownloadItem* item) OVERRIDE {}
306   virtual void OnDownloadRemoved(DownloadItem* item) OVERRIDE {}
307   virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE {}
308
309  private:
310   const base::FilePath temp_dir_;
311 };
312
313 void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
314   switch (item->GetState()) {
315     case DownloadItem::IN_PROGRESS:
316       break;
317     case DownloadItem::COMPLETE:
318     case DownloadItem::CANCELLED:
319     case DownloadItem::INTERRUPTED: {
320       item->RemoveObserver(this);
321       BrowserThread::DeleteOnFileThread::Destruct(this);
322       break;
323     }
324     default:
325       NOTREACHED();
326   }
327 }
328
329 FileDeleter::~FileDeleter() {
330   base::ScopedTempDir path;
331   bool will_delete ALLOW_UNUSED = path.Set(temp_dir_);
332   DCHECK(will_delete);
333 }
334
335 void IndexedDBInternalsUI::OnDownloadStarted(
336     const base::FilePath& partition_path,
337     const GURL& origin_url,
338     const base::FilePath& temp_path,
339     size_t connection_count,
340     DownloadItem* item,
341     net::Error error) {
342
343   if (error != net::OK) {
344     LOG(ERROR)
345         << "Error downloading database dump: " << net::ErrorToString(error);
346     return;
347   }
348
349   item->AddObserver(new FileDeleter(temp_path));
350   web_ui()->CallJavascriptFunction(
351       "indexeddb.onOriginDownloadReady",
352       base::StringValue(partition_path.value()),
353       base::StringValue(origin_url.spec()),
354       base::FundamentalValue(double(connection_count)));
355 }
356
357 }  // namespace content