Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_internals_ui.cc
blob2dfd6500466292185c93a6219e1bfacc43bc4d06
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 namespace {
32 bool AllowWhitelistedPaths(const std::vector<base::FilePath>& allowed_paths,
33 const base::FilePath& candidate_path) {
34 for (const base::FilePath& allowed_path : allowed_paths) {
35 if (allowed_path.IsParent(candidate_path))
36 return true;
38 return false;
41 } // namespace
43 IndexedDBInternalsUI::IndexedDBInternalsUI(WebUI* web_ui)
44 : WebUIController(web_ui) {
45 web_ui->RegisterMessageCallback(
46 "getAllOrigins",
47 base::Bind(&IndexedDBInternalsUI::GetAllOrigins, base::Unretained(this)));
49 web_ui->RegisterMessageCallback(
50 "downloadOriginData",
51 base::Bind(&IndexedDBInternalsUI::DownloadOriginData,
52 base::Unretained(this)));
53 web_ui->RegisterMessageCallback(
54 "forceClose",
55 base::Bind(&IndexedDBInternalsUI::ForceCloseOrigin,
56 base::Unretained(this)));
58 WebUIDataSource* source =
59 WebUIDataSource::Create(kChromeUIIndexedDBInternalsHost);
60 source->SetJsonPath("strings.js");
61 source->AddResourcePath("indexeddb_internals.js",
62 IDR_INDEXED_DB_INTERNALS_JS);
63 source->AddResourcePath("indexeddb_internals.css",
64 IDR_INDEXED_DB_INTERNALS_CSS);
65 source->SetDefaultResource(IDR_INDEXED_DB_INTERNALS_HTML);
67 BrowserContext* browser_context =
68 web_ui->GetWebContents()->GetBrowserContext();
69 WebUIDataSource::Add(browser_context, source);
72 IndexedDBInternalsUI::~IndexedDBInternalsUI() {}
74 void IndexedDBInternalsUI::AddContextFromStoragePartition(
75 StoragePartition* partition) {
76 scoped_refptr<IndexedDBContext> context = partition->GetIndexedDBContext();
77 context->TaskRunner()->PostTask(
78 FROM_HERE,
79 base::Bind(&IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread,
80 base::Unretained(this),
81 context,
82 partition->GetPath()));
85 void IndexedDBInternalsUI::GetAllOrigins(const base::ListValue* args) {
86 DCHECK_CURRENTLY_ON(BrowserThread::UI);
88 BrowserContext* browser_context =
89 web_ui()->GetWebContents()->GetBrowserContext();
91 BrowserContext::StoragePartitionCallback cb =
92 base::Bind(&IndexedDBInternalsUI::AddContextFromStoragePartition,
93 base::Unretained(this));
94 BrowserContext::ForEachStoragePartition(browser_context, cb);
97 void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
98 scoped_refptr<IndexedDBContext> context,
99 const base::FilePath& context_path) {
100 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
102 IndexedDBContextImpl* context_impl =
103 static_cast<IndexedDBContextImpl*>(context.get());
105 scoped_ptr<base::ListValue> info_list(context_impl->GetAllOriginsDetails());
106 bool is_incognito = context_impl->is_incognito();
108 BrowserThread::PostTask(
109 BrowserThread::UI,
110 FROM_HERE,
111 base::Bind(&IndexedDBInternalsUI::OnOriginsReady,
112 base::Unretained(this),
113 base::Passed(&info_list),
114 is_incognito ? base::FilePath() : context_path));
117 void IndexedDBInternalsUI::OnOriginsReady(scoped_ptr<base::ListValue> origins,
118 const base::FilePath& path) {
119 DCHECK_CURRENTLY_ON(BrowserThread::UI);
120 web_ui()->CallJavascriptFunction(
121 "indexeddb.onOriginsReady", *origins, base::StringValue(path.value()));
124 static void FindContext(const base::FilePath& partition_path,
125 StoragePartition** result_partition,
126 scoped_refptr<IndexedDBContextImpl>* result_context,
127 StoragePartition* storage_partition) {
128 if (storage_partition->GetPath() == partition_path) {
129 *result_partition = storage_partition;
130 *result_context = static_cast<IndexedDBContextImpl*>(
131 storage_partition->GetIndexedDBContext());
135 bool IndexedDBInternalsUI::GetOriginData(
136 const base::ListValue* args,
137 base::FilePath* partition_path,
138 GURL* origin_url,
139 scoped_refptr<IndexedDBContextImpl>* context) {
140 base::FilePath::StringType path_string;
141 if (!args->GetString(0, &path_string))
142 return false;
143 *partition_path = base::FilePath(path_string);
145 std::string url_string;
146 if (!args->GetString(1, &url_string))
147 return false;
149 *origin_url = GURL(url_string);
151 return GetOriginContext(*partition_path, *origin_url, context);
154 bool IndexedDBInternalsUI::GetOriginContext(
155 const base::FilePath& path,
156 const GURL& origin_url,
157 scoped_refptr<IndexedDBContextImpl>* context) {
158 // search the origins to find the right context
159 BrowserContext* browser_context =
160 web_ui()->GetWebContents()->GetBrowserContext();
162 StoragePartition* result_partition;
163 BrowserContext::StoragePartitionCallback cb =
164 base::Bind(&FindContext, path, &result_partition, context);
165 BrowserContext::ForEachStoragePartition(browser_context, cb);
167 if (!result_partition || !(context->get()))
168 return false;
170 return true;
173 void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) {
174 DCHECK_CURRENTLY_ON(BrowserThread::UI);
176 base::FilePath partition_path;
177 GURL origin_url;
178 scoped_refptr<IndexedDBContextImpl> context;
179 if (!GetOriginData(args, &partition_path, &origin_url, &context))
180 return;
182 DCHECK(context.get());
183 context->TaskRunner()->PostTask(
184 FROM_HERE,
185 base::Bind(&IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread,
186 base::Unretained(this),
187 partition_path,
188 context,
189 origin_url));
192 void IndexedDBInternalsUI::ForceCloseOrigin(const base::ListValue* args) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 base::FilePath partition_path;
196 GURL origin_url;
197 scoped_refptr<IndexedDBContextImpl> context;
198 if (!GetOriginData(args, &partition_path, &origin_url, &context))
199 return;
201 context->TaskRunner()->PostTask(
202 FROM_HERE,
203 base::Bind(&IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread,
204 base::Unretained(this),
205 partition_path,
206 context,
207 origin_url));
210 void IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread(
211 const base::FilePath& partition_path,
212 const scoped_refptr<IndexedDBContextImpl> context,
213 const GURL& origin_url) {
214 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
216 // Make sure the database hasn't been deleted since the page was loaded.
217 if (!context->IsInOriginSet(origin_url))
218 return;
220 context->ForceClose(origin_url,
221 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
222 size_t connection_count = context->GetConnectionCount(origin_url);
224 base::ScopedTempDir temp_dir;
225 if (!temp_dir.CreateUniqueTempDir())
226 return;
228 // This will get cleaned up on the File thread after the download
229 // has completed.
230 base::FilePath temp_path = temp_dir.Take();
232 std::string origin_id = storage::GetIdentifierFromOrigin(origin_url);
233 base::FilePath zip_path =
234 temp_path.AppendASCII(origin_id).AddExtension(FILE_PATH_LITERAL("zip"));
236 // This happens on the "webkit" thread (which is really just the IndexedDB
237 // thread) as a simple way to avoid another script reopening the origin
238 // while we are zipping.
239 std::vector<base::FilePath> paths = context->GetStoragePaths(origin_url);
240 zip::ZipWithFilterCallback(context->data_path(), zip_path,
241 base::Bind(AllowWhitelistedPaths, paths));
243 BrowserThread::PostTask(BrowserThread::UI,
244 FROM_HERE,
245 base::Bind(&IndexedDBInternalsUI::OnDownloadDataReady,
246 base::Unretained(this),
247 partition_path,
248 origin_url,
249 temp_path,
250 zip_path,
251 connection_count));
254 void IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread(
255 const base::FilePath& partition_path,
256 const scoped_refptr<IndexedDBContextImpl> context,
257 const GURL& origin_url) {
258 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
260 // Make sure the database hasn't been deleted since the page was loaded.
261 if (!context->IsInOriginSet(origin_url))
262 return;
264 context->ForceClose(origin_url,
265 IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
266 size_t connection_count = context->GetConnectionCount(origin_url);
268 BrowserThread::PostTask(BrowserThread::UI,
269 FROM_HERE,
270 base::Bind(&IndexedDBInternalsUI::OnForcedClose,
271 base::Unretained(this),
272 partition_path,
273 origin_url,
274 connection_count));
277 void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path,
278 const GURL& origin_url,
279 size_t connection_count) {
280 web_ui()->CallJavascriptFunction(
281 "indexeddb.onForcedClose",
282 base::StringValue(partition_path.value()),
283 base::StringValue(origin_url.spec()),
284 base::FundamentalValue(static_cast<double>(connection_count)));
287 void IndexedDBInternalsUI::OnDownloadDataReady(
288 const base::FilePath& partition_path,
289 const GURL& origin_url,
290 const base::FilePath temp_path,
291 const base::FilePath zip_path,
292 size_t connection_count) {
293 DCHECK_CURRENTLY_ON(BrowserThread::UI);
294 const GURL url = GURL(FILE_PATH_LITERAL("file://") + zip_path.value());
295 BrowserContext* browser_context =
296 web_ui()->GetWebContents()->GetBrowserContext();
297 scoped_ptr<DownloadUrlParameters> dl_params(
298 DownloadUrlParameters::FromWebContents(web_ui()->GetWebContents(), url));
299 DownloadManager* dlm = BrowserContext::GetDownloadManager(browser_context);
301 const GURL referrer(web_ui()->GetWebContents()->GetLastCommittedURL());
302 dl_params->set_referrer(content::Referrer::SanitizeForRequest(
303 url, content::Referrer(referrer, blink::WebReferrerPolicyDefault)));
305 // This is how to watch for the download to finish: first wait for it
306 // to start, then attach a DownloadItem::Observer to observe the
307 // state change to the finished state.
308 dl_params->set_callback(base::Bind(&IndexedDBInternalsUI::OnDownloadStarted,
309 base::Unretained(this),
310 partition_path,
311 origin_url,
312 temp_path,
313 connection_count));
314 dlm->DownloadUrl(dl_params.Pass());
317 // The entire purpose of this class is to delete the temp file after
318 // the download is complete.
319 class FileDeleter : public DownloadItem::Observer {
320 public:
321 explicit FileDeleter(const base::FilePath& temp_dir) : temp_dir_(temp_dir) {}
322 ~FileDeleter() override;
324 void OnDownloadUpdated(DownloadItem* download) override;
325 void OnDownloadOpened(DownloadItem* item) override {}
326 void OnDownloadRemoved(DownloadItem* item) override {}
327 void OnDownloadDestroyed(DownloadItem* item) override {}
329 private:
330 const base::FilePath temp_dir_;
332 DISALLOW_COPY_AND_ASSIGN(FileDeleter);
335 void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
336 switch (item->GetState()) {
337 case DownloadItem::IN_PROGRESS:
338 break;
339 case DownloadItem::COMPLETE:
340 case DownloadItem::CANCELLED:
341 case DownloadItem::INTERRUPTED: {
342 item->RemoveObserver(this);
343 BrowserThread::DeleteOnFileThread::Destruct(this);
344 break;
346 default:
347 NOTREACHED();
351 FileDeleter::~FileDeleter() {
352 base::ScopedTempDir path;
353 bool will_delete = path.Set(temp_dir_);
354 DCHECK(will_delete);
357 void IndexedDBInternalsUI::OnDownloadStarted(
358 const base::FilePath& partition_path,
359 const GURL& origin_url,
360 const base::FilePath& temp_path,
361 size_t connection_count,
362 DownloadItem* item,
363 DownloadInterruptReason interrupt_reason) {
365 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
366 LOG(ERROR) << "Error downloading database dump: "
367 << DownloadInterruptReasonToString(interrupt_reason);
368 return;
371 item->AddObserver(new FileDeleter(temp_path));
372 web_ui()->CallJavascriptFunction(
373 "indexeddb.onOriginDownloadReady",
374 base::StringValue(partition_path.value()),
375 base::StringValue(origin_url.spec()),
376 base::FundamentalValue(static_cast<double>(connection_count)));
379 } // namespace content