Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_internals_ui.cc
blobc63246ddb6b1d9bce0b5fc8d5f3afffab12216e1
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.
5 #include "content/browser/indexed_db/indexed_db_internals_ui.h"
7 #include <string>
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 "third_party/zlib/google/zip.h"
25 #include "ui/base/text/bytes_formatting.h"
26 #include "webkit/common/database/database_identifier.h"
28 namespace content {
30 IndexedDBInternalsUI::IndexedDBInternalsUI(WebUI* web_ui)
31 : WebUIController(web_ui) {
32 web_ui->RegisterMessageCallback(
33 "getAllOrigins",
34 base::Bind(&IndexedDBInternalsUI::GetAllOrigins, base::Unretained(this)));
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)));
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);
55 BrowserContext* browser_context =
56 web_ui->GetWebContents()->GetBrowserContext();
57 WebUIDataSource::Add(browser_context, source);
60 IndexedDBInternalsUI::~IndexedDBInternalsUI() {}
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()));
73 void IndexedDBInternalsUI::GetAllOrigins(const base::ListValue* args) {
74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76 BrowserContext* browser_context =
77 web_ui()->GetWebContents()->GetBrowserContext();
79 BrowserContext::StoragePartitionCallback cb =
80 base::Bind(&IndexedDBInternalsUI::AddContextFromStoragePartition,
81 base::Unretained(this));
82 BrowserContext::ForEachStoragePartition(browser_context, cb);
85 void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
86 scoped_refptr<IndexedDBContext> context,
87 const base::FilePath& context_path) {
88 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
90 IndexedDBContextImpl* context_impl =
91 static_cast<IndexedDBContextImpl*>(context.get());
93 scoped_ptr<base::ListValue> info_list(context_impl->GetAllOriginsDetails());
94 bool is_incognito = context_impl->is_incognito();
96 BrowserThread::PostTask(
97 BrowserThread::UI,
98 FROM_HERE,
99 base::Bind(&IndexedDBInternalsUI::OnOriginsReady,
100 base::Unretained(this),
101 base::Passed(&info_list),
102 is_incognito ? base::FilePath() : context_path));
105 void IndexedDBInternalsUI::OnOriginsReady(scoped_ptr<base::ListValue> origins,
106 const base::FilePath& path) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108 web_ui()->CallJavascriptFunction(
109 "indexeddb.onOriginsReady", *origins, base::StringValue(path.value()));
112 static void FindContext(const base::FilePath& partition_path,
113 StoragePartition** result_partition,
114 scoped_refptr<IndexedDBContextImpl>* result_context,
115 StoragePartition* storage_partition) {
116 if (storage_partition->GetPath() == partition_path) {
117 *result_partition = storage_partition;
118 *result_context = static_cast<IndexedDBContextImpl*>(
119 storage_partition->GetIndexedDBContext());
123 bool IndexedDBInternalsUI::GetOriginData(
124 const base::ListValue* args,
125 base::FilePath* partition_path,
126 GURL* origin_url,
127 scoped_refptr<IndexedDBContextImpl>* context) {
128 base::FilePath::StringType path_string;
129 if (!args->GetString(0, &path_string))
130 return false;
131 *partition_path = base::FilePath(path_string);
133 std::string url_string;
134 if (!args->GetString(1, &url_string))
135 return false;
137 *origin_url = GURL(url_string);
139 return GetOriginContext(*partition_path, *origin_url, context);
142 bool IndexedDBInternalsUI::GetOriginContext(
143 const base::FilePath& path,
144 const GURL& origin_url,
145 scoped_refptr<IndexedDBContextImpl>* context) {
146 // search the origins to find the right context
147 BrowserContext* browser_context =
148 web_ui()->GetWebContents()->GetBrowserContext();
150 StoragePartition* result_partition;
151 BrowserContext::StoragePartitionCallback cb =
152 base::Bind(&FindContext, path, &result_partition, context);
153 BrowserContext::ForEachStoragePartition(browser_context, cb);
155 if (!result_partition || !(context->get()))
156 return false;
158 return true;
161 void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164 base::FilePath partition_path;
165 GURL origin_url;
166 scoped_refptr<IndexedDBContextImpl> context;
167 if (!GetOriginData(args, &partition_path, &origin_url, &context))
168 return;
170 DCHECK(context.get());
171 context->TaskRunner()->PostTask(
172 FROM_HERE,
173 base::Bind(&IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread,
174 base::Unretained(this),
175 partition_path,
176 context,
177 origin_url));
180 void IndexedDBInternalsUI::ForceCloseOrigin(const base::ListValue* args) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 base::FilePath partition_path;
184 GURL origin_url;
185 scoped_refptr<IndexedDBContextImpl> context;
186 if (!GetOriginData(args, &partition_path, &origin_url, &context))
187 return;
189 context->TaskRunner()->PostTask(
190 FROM_HERE,
191 base::Bind(&IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread,
192 base::Unretained(this),
193 partition_path,
194 context,
195 origin_url));
198 void IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread(
199 const base::FilePath& partition_path,
200 const scoped_refptr<IndexedDBContextImpl> context,
201 const GURL& origin_url) {
202 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
204 // Make sure the database hasn't been deleted since the page was loaded.
205 if (!context->IsInOriginSet(origin_url))
206 return;
208 context->ForceClose(origin_url,
209 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
210 size_t connection_count = context->GetConnectionCount(origin_url);
212 base::ScopedTempDir temp_dir;
213 if (!temp_dir.CreateUniqueTempDir())
214 return;
216 // This will get cleaned up on the File thread after the download
217 // has completed.
218 base::FilePath temp_path = temp_dir.Take();
220 std::string origin_id = storage::GetIdentifierFromOrigin(origin_url);
221 base::FilePath zip_path =
222 temp_path.AppendASCII(origin_id).AddExtension(FILE_PATH_LITERAL("zip"));
224 // This happens on the "webkit" thread (which is really just the IndexedDB
225 // thread) as a simple way to avoid another script reopening the origin
226 // while we are zipping.
227 zip::Zip(context->GetFilePath(origin_url), zip_path, true);
229 BrowserThread::PostTask(BrowserThread::UI,
230 FROM_HERE,
231 base::Bind(&IndexedDBInternalsUI::OnDownloadDataReady,
232 base::Unretained(this),
233 partition_path,
234 origin_url,
235 temp_path,
236 zip_path,
237 connection_count));
240 void IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread(
241 const base::FilePath& partition_path,
242 const scoped_refptr<IndexedDBContextImpl> context,
243 const GURL& origin_url) {
244 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
246 // Make sure the database hasn't been deleted since the page was loaded.
247 if (!context->IsInOriginSet(origin_url))
248 return;
250 context->ForceClose(origin_url,
251 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
252 size_t connection_count = context->GetConnectionCount(origin_url);
254 BrowserThread::PostTask(BrowserThread::UI,
255 FROM_HERE,
256 base::Bind(&IndexedDBInternalsUI::OnForcedClose,
257 base::Unretained(this),
258 partition_path,
259 origin_url,
260 connection_count));
263 void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path,
264 const GURL& origin_url,
265 size_t connection_count) {
266 web_ui()->CallJavascriptFunction(
267 "indexeddb.onForcedClose",
268 base::StringValue(partition_path.value()),
269 base::StringValue(origin_url.spec()),
270 base::FundamentalValue(static_cast<double>(connection_count)));
273 void IndexedDBInternalsUI::OnDownloadDataReady(
274 const base::FilePath& partition_path,
275 const GURL& origin_url,
276 const base::FilePath temp_path,
277 const base::FilePath zip_path,
278 size_t connection_count) {
279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
280 const GURL url = GURL(FILE_PATH_LITERAL("file://") + zip_path.value());
281 BrowserContext* browser_context =
282 web_ui()->GetWebContents()->GetBrowserContext();
283 scoped_ptr<DownloadUrlParameters> dl_params(
284 DownloadUrlParameters::FromWebContents(web_ui()->GetWebContents(), url));
285 DownloadManager* dlm = BrowserContext::GetDownloadManager(browser_context);
287 const GURL referrer(web_ui()->GetWebContents()->GetLastCommittedURL());
288 dl_params->set_referrer(
289 content::Referrer(referrer, blink::WebReferrerPolicyDefault));
291 // This is how to watch for the download to finish: first wait for it
292 // to start, then attach a DownloadItem::Observer to observe the
293 // state change to the finished state.
294 dl_params->set_callback(base::Bind(&IndexedDBInternalsUI::OnDownloadStarted,
295 base::Unretained(this),
296 partition_path,
297 origin_url,
298 temp_path,
299 connection_count));
300 dlm->DownloadUrl(dl_params.Pass());
303 // The entire purpose of this class is to delete the temp file after
304 // the download is complete.
305 class FileDeleter : public DownloadItem::Observer {
306 public:
307 explicit FileDeleter(const base::FilePath& temp_dir) : temp_dir_(temp_dir) {}
308 virtual ~FileDeleter();
310 virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE;
311 virtual void OnDownloadOpened(DownloadItem* item) OVERRIDE {}
312 virtual void OnDownloadRemoved(DownloadItem* item) OVERRIDE {}
313 virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE {}
315 private:
316 const base::FilePath temp_dir_;
318 DISALLOW_COPY_AND_ASSIGN(FileDeleter);
321 void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
322 switch (item->GetState()) {
323 case DownloadItem::IN_PROGRESS:
324 break;
325 case DownloadItem::COMPLETE:
326 case DownloadItem::CANCELLED:
327 case DownloadItem::INTERRUPTED: {
328 item->RemoveObserver(this);
329 BrowserThread::DeleteOnFileThread::Destruct(this);
330 break;
332 default:
333 NOTREACHED();
337 FileDeleter::~FileDeleter() {
338 base::ScopedTempDir path;
339 bool will_delete ALLOW_UNUSED = path.Set(temp_dir_);
340 DCHECK(will_delete);
343 void IndexedDBInternalsUI::OnDownloadStarted(
344 const base::FilePath& partition_path,
345 const GURL& origin_url,
346 const base::FilePath& temp_path,
347 size_t connection_count,
348 DownloadItem* item,
349 DownloadInterruptReason interrupt_reason) {
351 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
352 LOG(ERROR) << "Error downloading database dump: "
353 << DownloadInterruptReasonToString(interrupt_reason);
354 return;
357 item->AddObserver(new FileDeleter(temp_path));
358 web_ui()->CallJavascriptFunction(
359 "indexeddb.onOriginDownloadReady",
360 base::StringValue(partition_path.value()),
361 base::StringValue(origin_url.spec()),
362 base::FundamentalValue(static_cast<double>(connection_count)));
365 } // namespace content