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