Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_file_helper.cc
blob11f270bb9231f5f0d8e7eb4269bbb7a4e08b4dee
1 // Copyright (c) 2012 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 "chrome/browser/devtools/devtools_file_helper.h"
7 #include <set>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/md5.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/prefs/scoped_user_pref_update.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/value_conversions.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/download/download_prefs.h"
21 #include "chrome/browser/platform_util.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/chrome_select_file_policy.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "content/public/browser/browser_context.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/child_process_security_policy.h"
29 #include "content/public/browser/download_manager.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/common/content_client.h"
34 #include "content/public/common/url_constants.h"
35 #include "storage/browser/fileapi/file_system_url.h"
36 #include "storage/browser/fileapi/isolated_context.h"
37 #include "storage/common/fileapi/file_system_util.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/shell_dialogs/select_file_dialog.h"
41 using base::Bind;
42 using base::Callback;
43 using content::BrowserContext;
44 using content::BrowserThread;
45 using content::DownloadManager;
46 using content::RenderViewHost;
47 using content::WebContents;
48 using std::set;
50 namespace {
52 base::LazyInstance<base::FilePath>::Leaky
53 g_last_save_path = LAZY_INSTANCE_INITIALIZER;
55 } // namespace
57 namespace {
59 typedef Callback<void(const base::FilePath&)> SelectedCallback;
60 typedef Callback<void(void)> CanceledCallback;
62 class SelectFileDialog : public ui::SelectFileDialog::Listener,
63 public base::RefCounted<SelectFileDialog> {
64 public:
65 SelectFileDialog(const SelectedCallback& selected_callback,
66 const CanceledCallback& canceled_callback,
67 WebContents* web_contents)
68 : selected_callback_(selected_callback),
69 canceled_callback_(canceled_callback),
70 web_contents_(web_contents) {
71 select_file_dialog_ = ui::SelectFileDialog::Create(
72 this, new ChromeSelectFilePolicy(web_contents));
75 void Show(ui::SelectFileDialog::Type type,
76 const base::FilePath& default_path) {
77 AddRef(); // Balanced in the three listener outcomes.
78 select_file_dialog_->SelectFile(
79 type,
80 base::string16(),
81 default_path,
82 NULL,
84 base::FilePath::StringType(),
85 platform_util::GetTopLevel(web_contents_->GetNativeView()),
86 NULL);
89 // ui::SelectFileDialog::Listener implementation.
90 void FileSelected(const base::FilePath& path,
91 int index,
92 void* params) override {
93 selected_callback_.Run(path);
94 Release(); // Balanced in ::Show.
97 void MultiFilesSelected(const std::vector<base::FilePath>& files,
98 void* params) override {
99 Release(); // Balanced in ::Show.
100 NOTREACHED() << "Should not be able to select multiple files";
103 void FileSelectionCanceled(void* params) override {
104 canceled_callback_.Run();
105 Release(); // Balanced in ::Show.
108 private:
109 friend class base::RefCounted<SelectFileDialog>;
110 ~SelectFileDialog() override {}
112 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
113 SelectedCallback selected_callback_;
114 CanceledCallback canceled_callback_;
115 WebContents* web_contents_;
117 DISALLOW_COPY_AND_ASSIGN(SelectFileDialog);
120 void WriteToFile(const base::FilePath& path, const std::string& content) {
121 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
122 DCHECK(!path.empty());
124 base::WriteFile(path, content.c_str(), content.length());
127 void AppendToFile(const base::FilePath& path, const std::string& content) {
128 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
129 DCHECK(!path.empty());
131 base::AppendToFile(path, content.c_str(), content.size());
134 storage::IsolatedContext* isolated_context() {
135 DCHECK_CURRENTLY_ON(BrowserThread::UI);
136 storage::IsolatedContext* isolated_context =
137 storage::IsolatedContext::GetInstance();
138 DCHECK(isolated_context);
139 return isolated_context;
142 std::string RegisterFileSystem(WebContents* web_contents,
143 const base::FilePath& path,
144 std::string* registered_name) {
145 DCHECK_CURRENTLY_ON(BrowserThread::UI);
146 CHECK(web_contents->GetURL().SchemeIs(content::kChromeDevToolsScheme));
147 std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
148 storage::kFileSystemTypeNativeLocal,
149 std::string(),
150 path,
151 registered_name);
153 content::ChildProcessSecurityPolicy* policy =
154 content::ChildProcessSecurityPolicy::GetInstance();
155 RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
156 int renderer_id = render_view_host->GetProcess()->GetID();
157 policy->GrantReadFileSystem(renderer_id, file_system_id);
158 policy->GrantWriteFileSystem(renderer_id, file_system_id);
159 policy->GrantCreateFileForFileSystem(renderer_id, file_system_id);
160 policy->GrantDeleteFromFileSystem(renderer_id, file_system_id);
162 // We only need file level access for reading FileEntries. Saving FileEntries
163 // just needs the file system to have read/write access, which is granted
164 // above if required.
165 if (!policy->CanReadFile(renderer_id, path))
166 policy->GrantReadFile(renderer_id, path);
168 return file_system_id;
171 DevToolsFileHelper::FileSystem CreateFileSystemStruct(
172 WebContents* web_contents,
173 const std::string& file_system_id,
174 const std::string& registered_name,
175 const std::string& file_system_path) {
176 const GURL origin = web_contents->GetURL().GetOrigin();
177 std::string file_system_name =
178 storage::GetIsolatedFileSystemName(origin, file_system_id);
179 std::string root_url = storage::GetIsolatedFileSystemRootURIString(
180 origin, file_system_id, registered_name);
181 return DevToolsFileHelper::FileSystem(file_system_name,
182 root_url,
183 file_system_path);
186 set<std::string> GetAddedFileSystemPaths(Profile* profile) {
187 const base::DictionaryValue* file_systems_paths_value =
188 profile->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
189 set<std::string> result;
190 for (base::DictionaryValue::Iterator it(*file_systems_paths_value);
191 !it.IsAtEnd(); it.Advance()) {
192 result.insert(it.key());
194 return result;
197 } // namespace
199 DevToolsFileHelper::FileSystem::FileSystem() {
202 DevToolsFileHelper::FileSystem::FileSystem(const std::string& file_system_name,
203 const std::string& root_url,
204 const std::string& file_system_path)
205 : file_system_name(file_system_name),
206 root_url(root_url),
207 file_system_path(file_system_path) {
210 DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents,
211 Profile* profile)
212 : web_contents_(web_contents),
213 profile_(profile),
214 weak_factory_(this) {
217 DevToolsFileHelper::~DevToolsFileHelper() {
220 void DevToolsFileHelper::Save(const std::string& url,
221 const std::string& content,
222 bool save_as,
223 const SaveCallback& saveCallback,
224 const SaveCallback& cancelCallback) {
225 PathsMap::iterator it = saved_files_.find(url);
226 if (it != saved_files_.end() && !save_as) {
227 SaveAsFileSelected(url, content, saveCallback, it->second);
228 return;
231 const base::DictionaryValue* file_map =
232 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles);
233 base::FilePath initial_path;
235 const base::Value* path_value;
236 if (file_map->Get(base::MD5String(url), &path_value))
237 base::GetValueAsFilePath(*path_value, &initial_path);
239 if (initial_path.empty()) {
240 GURL gurl(url);
241 std::string suggested_file_name = gurl.is_valid() ?
242 gurl.ExtractFileName() : url;
244 if (suggested_file_name.length() > 64)
245 suggested_file_name = suggested_file_name.substr(0, 64);
247 if (!g_last_save_path.Pointer()->empty()) {
248 initial_path = g_last_save_path.Pointer()->DirName().AppendASCII(
249 suggested_file_name);
250 } else {
251 base::FilePath download_path = DownloadPrefs::FromDownloadManager(
252 BrowserContext::GetDownloadManager(profile_))->DownloadPath();
253 initial_path = download_path.AppendASCII(suggested_file_name);
257 scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
258 Bind(&DevToolsFileHelper::SaveAsFileSelected,
259 weak_factory_.GetWeakPtr(),
260 url,
261 content,
262 saveCallback),
263 Bind(&DevToolsFileHelper::SaveAsFileSelectionCanceled,
264 weak_factory_.GetWeakPtr(),
265 cancelCallback),
266 web_contents_);
267 select_file_dialog->Show(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
268 initial_path);
271 void DevToolsFileHelper::Append(const std::string& url,
272 const std::string& content,
273 const AppendCallback& callback) {
274 PathsMap::iterator it = saved_files_.find(url);
275 if (it == saved_files_.end())
276 return;
277 callback.Run();
278 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
279 Bind(&AppendToFile, it->second, content));
282 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
283 const std::string& content,
284 const SaveCallback& callback,
285 const base::FilePath& path) {
286 *g_last_save_path.Pointer() = path;
287 saved_files_[url] = path;
289 DictionaryPrefUpdate update(profile_->GetPrefs(),
290 prefs::kDevToolsEditedFiles);
291 base::DictionaryValue* files_map = update.Get();
292 files_map->SetWithoutPathExpansion(base::MD5String(url),
293 base::CreateFilePathValue(path));
294 callback.Run();
295 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
296 Bind(&WriteToFile, path, content));
299 void DevToolsFileHelper::SaveAsFileSelectionCanceled(
300 const SaveCallback& callback) {
301 callback.Run();
304 void DevToolsFileHelper::AddFileSystem(
305 const AddFileSystemCallback& callback,
306 const ShowInfoBarCallback& show_info_bar_callback) {
307 scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
308 Bind(&DevToolsFileHelper::InnerAddFileSystem,
309 weak_factory_.GetWeakPtr(),
310 callback,
311 show_info_bar_callback),
312 Bind(callback, FileSystem()),
313 web_contents_);
314 select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER,
315 base::FilePath());
318 void DevToolsFileHelper::UpgradeDraggedFileSystemPermissions(
319 const std::string& file_system_url,
320 const AddFileSystemCallback& callback,
321 const ShowInfoBarCallback& show_info_bar_callback) {
322 storage::FileSystemURL root_url =
323 isolated_context()->CrackURL(GURL(file_system_url));
324 if (!root_url.is_valid() || !root_url.path().empty()) {
325 callback.Run(FileSystem());
326 return;
329 std::vector<storage::MountPoints::MountPointInfo> mount_points;
330 isolated_context()->GetDraggedFileInfo(root_url.filesystem_id(),
331 &mount_points);
333 std::vector<storage::MountPoints::MountPointInfo>::const_iterator it =
334 mount_points.begin();
335 for (; it != mount_points.end(); ++it)
336 InnerAddFileSystem(callback, show_info_bar_callback, it->path);
339 void DevToolsFileHelper::InnerAddFileSystem(
340 const AddFileSystemCallback& callback,
341 const ShowInfoBarCallback& show_info_bar_callback,
342 const base::FilePath& path) {
343 std::string file_system_path = path.AsUTF8Unsafe();
345 const base::DictionaryValue* file_systems_paths_value =
346 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
347 if (file_systems_paths_value->HasKey(file_system_path)) {
348 callback.Run(FileSystem());
349 return;
352 std::string path_display_name = path.AsEndingWithSeparator().AsUTF8Unsafe();
353 base::string16 message = l10n_util::GetStringFUTF16(
354 IDS_DEV_TOOLS_CONFIRM_ADD_FILE_SYSTEM_MESSAGE,
355 base::UTF8ToUTF16(path_display_name));
356 show_info_bar_callback.Run(
357 message,
358 Bind(&DevToolsFileHelper::AddUserConfirmedFileSystem,
359 weak_factory_.GetWeakPtr(),
360 callback, path));
363 void DevToolsFileHelper::AddUserConfirmedFileSystem(
364 const AddFileSystemCallback& callback,
365 const base::FilePath& path,
366 bool allowed) {
367 if (!allowed) {
368 callback.Run(FileSystem());
369 return;
371 std::string registered_name;
372 std::string file_system_id = RegisterFileSystem(web_contents_,
373 path,
374 &registered_name);
375 std::string file_system_path = path.AsUTF8Unsafe();
377 DictionaryPrefUpdate update(profile_->GetPrefs(),
378 prefs::kDevToolsFileSystemPaths);
379 base::DictionaryValue* file_systems_paths_value = update.Get();
380 file_systems_paths_value->SetWithoutPathExpansion(
381 file_system_path, base::Value::CreateNullValue());
383 FileSystem filesystem = CreateFileSystemStruct(web_contents_,
384 file_system_id,
385 registered_name,
386 file_system_path);
387 callback.Run(filesystem);
390 void DevToolsFileHelper::RequestFileSystems(
391 const RequestFileSystemsCallback& callback) {
392 set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
393 set<std::string>::const_iterator it = file_system_paths.begin();
394 std::vector<FileSystem> file_systems;
395 for (; it != file_system_paths.end(); ++it) {
396 std::string file_system_path = *it;
397 base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
399 std::string registered_name;
400 std::string file_system_id = RegisterFileSystem(web_contents_,
401 path,
402 &registered_name);
403 FileSystem filesystem = CreateFileSystemStruct(web_contents_,
404 file_system_id,
405 registered_name,
406 file_system_path);
407 file_systems.push_back(filesystem);
409 callback.Run(file_systems);
412 void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
413 DCHECK_CURRENTLY_ON(BrowserThread::UI);
414 base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
415 isolated_context()->RevokeFileSystemByPath(path);
417 DictionaryPrefUpdate update(profile_->GetPrefs(),
418 prefs::kDevToolsFileSystemPaths);
419 base::DictionaryValue* file_systems_paths_value = update.Get();
420 file_systems_paths_value->RemoveWithoutPathExpansion(file_system_path, NULL);
423 bool DevToolsFileHelper::IsFileSystemAdded(
424 const std::string& file_system_path) {
425 DCHECK_CURRENTLY_ON(BrowserThread::UI);
426 set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
427 return file_system_paths.find(file_system_path) != file_system_paths.end();