Apply _RELATIVE relocations ahead of others.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_internals_ui.cc
blob472e1b9abcf3109be6d170d7d8b171beb83c529d
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 "storage/common/database/database_identifier.h"
25 #include "third_party/zlib/google/zip.h"
26 #include "ui/base/text/bytes_formatting.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->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);
54 BrowserContext* browser_context =
55 web_ui->GetWebContents()->GetBrowserContext();
56 WebUIDataSource::Add(browser_context, source);
59 IndexedDBInternalsUI::~IndexedDBInternalsUI() {}
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()));
72 void IndexedDBInternalsUI::GetAllOrigins(const base::ListValue* args) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75 BrowserContext* browser_context =
76 web_ui()->GetWebContents()->GetBrowserContext();
78 BrowserContext::StoragePartitionCallback cb =
79 base::Bind(&IndexedDBInternalsUI::AddContextFromStoragePartition,
80 base::Unretained(this));
81 BrowserContext::ForEachStoragePartition(browser_context, cb);
84 void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
85 scoped_refptr<IndexedDBContext> context,
86 const base::FilePath& context_path) {
87 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
89 IndexedDBContextImpl* context_impl =
90 static_cast<IndexedDBContextImpl*>(context.get());
92 scoped_ptr<base::ListValue> info_list(context_impl->GetAllOriginsDetails());
93 bool is_incognito = context_impl->is_incognito();
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));
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()));
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());
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);
132 std::string url_string;
133 if (!args->GetString(1, &url_string))
134 return false;
136 *origin_url = GURL(url_string);
138 return GetOriginContext(*partition_path, *origin_url, context);
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();
149 StoragePartition* result_partition;
150 BrowserContext::StoragePartitionCallback cb =
151 base::Bind(&FindContext, path, &result_partition, context);
152 BrowserContext::ForEachStoragePartition(browser_context, cb);
154 if (!result_partition || !(context->get()))
155 return false;
157 return true;
160 void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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;
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));
179 void IndexedDBInternalsUI::ForceCloseOrigin(const base::ListValue* args) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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;
188 context->TaskRunner()->PostTask(
189 FROM_HERE,
190 base::Bind(&IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread,
191 base::Unretained(this),
192 partition_path,
193 context,
194 origin_url));
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());
203 // Make sure the database hasn't been deleted since the page was loaded.
204 if (!context->IsInOriginSet(origin_url))
205 return;
207 context->ForceClose(origin_url,
208 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
209 size_t connection_count = context->GetConnectionCount(origin_url);
211 base::ScopedTempDir temp_dir;
212 if (!temp_dir.CreateUniqueTempDir())
213 return;
215 // This will get cleaned up on the File thread after the download
216 // has completed.
217 base::FilePath temp_path = temp_dir.Take();
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"));
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);
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));
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());
245 // Make sure the database hasn't been deleted since the page was loaded.
246 if (!context->IsInOriginSet(origin_url))
247 return;
249 context->ForceClose(origin_url,
250 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
251 size_t connection_count = context->GetConnectionCount(origin_url);
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));
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)));
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);
286 const GURL referrer(web_ui()->GetWebContents()->GetLastCommittedURL());
287 dl_params->set_referrer(
288 content::Referrer(referrer, blink::WebReferrerPolicyDefault));
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());
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;
309 void OnDownloadUpdated(DownloadItem* download) override;
310 void OnDownloadOpened(DownloadItem* item) override {}
311 void OnDownloadRemoved(DownloadItem* item) override {}
312 void OnDownloadDestroyed(DownloadItem* item) override {}
314 private:
315 const base::FilePath temp_dir_;
317 DISALLOW_COPY_AND_ASSIGN(FileDeleter);
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;
331 default:
332 NOTREACHED();
336 FileDeleter::~FileDeleter() {
337 base::ScopedTempDir path;
338 bool will_delete = path.Set(temp_dir_);
339 DCHECK(will_delete);
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) {
350 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
351 LOG(ERROR) << "Error downloading database dump: "
352 << DownloadInterruptReasonToString(interrupt_reason);
353 return;
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)));
364 } // namespace content