Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(190)

Side by Side Diff: chrome/browser/devtools/devtools_file_helper.cc

Issue 11570081: Support file system access in DevTools with isolated file system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added ALLOW_THIS_IN_INITIALIZER_LIST, fixed nits. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/devtools/devtools_file_helper.h" 5 #include "chrome/browser/devtools/devtools_file_helper.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/lazy_instance.h" 12 #include "base/lazy_instance.h"
13 #include "base/md5.h" 13 #include "base/md5.h"
14 #include "base/utf_string_conversions.h"
14 #include "base/value_conversions.h" 15 #include "base/value_conversions.h"
15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/download/download_prefs.h" 17 #include "chrome/browser/download/download_prefs.h"
17 #include "chrome/browser/prefs/pref_service.h" 18 #include "chrome/browser/prefs/pref_service.h"
18 #include "chrome/browser/prefs/scoped_user_pref_update.h" 19 #include "chrome/browser/prefs/scoped_user_pref_update.h"
19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/chrome_select_file_policy.h" 21 #include "chrome/browser/ui/chrome_select_file_policy.h"
21 #include "chrome/common/pref_names.h" 22 #include "chrome/common/pref_names.h"
22 #include "content/public/browser/browser_context.h" 23 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/child_process_security_policy.h"
24 #include "content/public/browser/download_manager.h" 26 #include "content/public/browser/download_manager.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/content_client.h"
31 #include "grit/generated_resources.h"
25 #include "ui/base/dialogs/select_file_dialog.h" 32 #include "ui/base/dialogs/select_file_dialog.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "webkit/fileapi/isolated_context.h"
26 35
27 using base::Bind; 36 using base::Bind;
28 using base::Callback; 37 using base::Callback;
29 using content::BrowserContext; 38 using content::BrowserContext;
30 using content::BrowserThread; 39 using content::BrowserThread;
31 using content::DownloadManager; 40 using content::DownloadManager;
41 using content::RenderViewHost;
42 using content::WebContents;
32 43
33 namespace { 44 namespace {
34 45
35 base::LazyInstance<FilePath>::Leaky 46 base::LazyInstance<FilePath>::Leaky
36 g_last_save_path = LAZY_INSTANCE_INITIALIZER; 47 g_last_save_path = LAZY_INSTANCE_INITIALIZER;
37 48
38 } // namespace 49 } // namespace
39 50
40 namespace { 51 namespace {
41 52
42 typedef Callback<void(const FilePath&)> SelectedCallback; 53 typedef Callback<void(const FilePath&)> SelectedCallback;
43 typedef Callback<void(void)> CanceledCallback; 54 typedef Callback<void(void)> CanceledCallback;
44 55
56 const FilePath::CharType kMagicFileName[] =
Tom Sepez 2013/01/11 18:34:17 How does this file get created? Manually, outside
57 FILE_PATH_LITERAL(".allow-devtools-edit");
58
45 class SelectFileDialog : public ui::SelectFileDialog::Listener, 59 class SelectFileDialog : public ui::SelectFileDialog::Listener,
46 public base::RefCounted<SelectFileDialog> { 60 public base::RefCounted<SelectFileDialog> {
47 public: 61 public:
48 SelectFileDialog(const SelectedCallback& selected_callback, 62 SelectFileDialog(const SelectedCallback& selected_callback,
49 const CanceledCallback& canceled_callback) 63 const CanceledCallback& canceled_callback)
50 : selected_callback_(selected_callback), 64 : selected_callback_(selected_callback),
51 canceled_callback_(canceled_callback) { 65 canceled_callback_(canceled_callback) {
52 select_file_dialog_ = ui::SelectFileDialog::Create( 66 select_file_dialog_ = ui::SelectFileDialog::Create(
53 this, new ChromeSelectFilePolicy(NULL)); 67 this, new ChromeSelectFilePolicy(NULL));
54 } 68 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 file_util::WriteFile(path, content.c_str(), content.length()); 113 file_util::WriteFile(path, content.c_str(), content.length());
100 } 114 }
101 115
102 void AppendToFile(const FilePath& path, const std::string& content) { 116 void AppendToFile(const FilePath& path, const std::string& content) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
104 DCHECK(!path.empty()); 118 DCHECK(!path.empty());
105 119
106 file_util::AppendToFile(path, content.c_str(), content.length()); 120 file_util::AppendToFile(path, content.c_str(), content.length());
107 } 121 }
108 122
123 fileapi::IsolatedContext* isolated_context() {
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
125 fileapi::IsolatedContext* isolated_context =
126 fileapi::IsolatedContext::GetInstance();
127 DCHECK(isolated_context);
128 return isolated_context;
129 }
130
131 std::string RegisterFileSystem(WebContents* web_contents,
Tom Sepez 2013/01/11 18:40:08 Maybe put the magic file check here so there can't
132 const FilePath& path,
133 std::string* registered_name) {
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135 CHECK(content::GetContentClient()->HasWebUIScheme(web_contents->GetURL()));
136 std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
137 fileapi::kFileSystemTypeNativeLocal, path, registered_name);
138
139 content::ChildProcessSecurityPolicy* policy =
140 content::ChildProcessSecurityPolicy::GetInstance();
141 RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
142 int renderer_id = render_view_host->GetProcess()->GetID();
143 policy->GrantReadWriteFileSystem(renderer_id, file_system_id);
144
145 // We only need file level access for reading FileEntries. Saving FileEntries
146 // just needs the file system to have read/write access, which is granted
147 // above if required.
148 if (!policy->CanReadFile(renderer_id, path))
149 policy->GrantReadFile(renderer_id, path);
150
151 return file_system_id;
152 }
153
154 typedef Callback<void(const std::vector<FilePath>&)> ValidateFoldersCallback;
155
156 void ValidateFoldersOnFileThread(const std::vector<FilePath>& file_paths,
157 const ValidateFoldersCallback& callback) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
159 std::vector<FilePath> permitted_paths;
160 std::vector<FilePath>::const_iterator it;
161 for (it = file_paths.begin(); it != file_paths.end(); ++it) {
162 FilePath security_file_path = it->Append(kMagicFileName);
163 if (file_util::PathExists(security_file_path))
164 permitted_paths.push_back(*it);
165 }
166 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
167 Bind(callback, permitted_paths));
168 }
169
109 } // namespace 170 } // namespace
110 171
111 DevToolsFileHelper::DevToolsFileHelper(Profile* profile) : profile_(profile), 172 DevToolsFileHelper::FileSystem::FileSystem() {
112 weak_factory_(this) { 173 }
174
175 DevToolsFileHelper::FileSystem::FileSystem(const std::string& file_system_id,
176 const std::string& registered_name,
177 const std::string& file_system_path)
178 : file_system_id(file_system_id),
179 registered_name(registered_name),
180 file_system_path(file_system_path) {
181 }
182
183 DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents,
184 Profile* profile)
185 : web_contents_(web_contents),
186 profile_(profile),
187 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
113 } 188 }
114 189
115 DevToolsFileHelper::~DevToolsFileHelper() { 190 DevToolsFileHelper::~DevToolsFileHelper() {
116 } 191 }
117 192
118 void DevToolsFileHelper::Save(const std::string& url, 193 void DevToolsFileHelper::Save(const std::string& url,
119 const std::string& content, 194 const std::string& content,
120 bool save_as, 195 bool save_as,
121 const SaveCallback& callback) { 196 const SaveCallback& callback) {
122 PathsMap::iterator it = saved_files_.find(url); 197 PathsMap::iterator it = saved_files_.find(url);
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 } 239 }
165 240
166 void DevToolsFileHelper::Append(const std::string& url, 241 void DevToolsFileHelper::Append(const std::string& url,
167 const std::string& content, 242 const std::string& content,
168 const AppendCallback& callback) { 243 const AppendCallback& callback) {
169 PathsMap::iterator it = saved_files_.find(url); 244 PathsMap::iterator it = saved_files_.find(url);
170 if (it == saved_files_.end()) 245 if (it == saved_files_.end())
171 return; 246 return;
172 callback.Run(); 247 callback.Run();
173 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 248 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
174 base::Bind(&AppendToFile, it->second, content)); 249 Bind(&AppendToFile, it->second, content));
175 } 250 }
176 251
177 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url, 252 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
178 const std::string& content, 253 const std::string& content,
179 const SaveCallback& callback, 254 const SaveCallback& callback,
180 const FilePath& path) { 255 const FilePath& path) {
181 *g_last_save_path.Pointer() = path; 256 *g_last_save_path.Pointer() = path;
182 saved_files_[url] = path; 257 saved_files_[url] = path;
183 258
184 DictionaryPrefUpdate update(profile_->GetPrefs(), 259 DictionaryPrefUpdate update(profile_->GetPrefs(),
185 prefs::kDevToolsEditedFiles); 260 prefs::kDevToolsEditedFiles);
186 DictionaryValue* files_map = update.Get(); 261 DictionaryValue* files_map = update.Get();
187 files_map->SetWithoutPathExpansion(base::MD5String(url), 262 files_map->SetWithoutPathExpansion(base::MD5String(url),
188 base::CreateFilePathValue(path)); 263 base::CreateFilePathValue(path));
189 callback.Run(); 264 callback.Run();
190 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 265 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
191 base::Bind(&WriteToFile, path, content)); 266 Bind(&WriteToFile, path, content));
192 } 267 }
193 268
194 void DevToolsFileHelper::SaveAsFileSelectionCanceled() { 269 void DevToolsFileHelper::SaveAsFileSelectionCanceled() {
195 } 270 }
271
272 void DevToolsFileHelper::AddFileSystem(const AddFileSystemCallback& callback) {
273 scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
274 Bind(&DevToolsFileHelper::InnerAddFileSystem,
275 weak_factory_.GetWeakPtr(),
276 callback),
277 Bind(callback, "", FileSystem()));
278 select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER, FilePath());
279 }
280
281 void DevToolsFileHelper::InnerAddFileSystem(
282 const AddFileSystemCallback& callback,
283 const FilePath& path) {
284 std::vector<FilePath> file_paths(1, path);
285 ValidateFoldersCallback validate_folders_callback = Bind(
286 &DevToolsFileHelper::AddValidatedFileSystem,
287 weak_factory_.GetWeakPtr(),
288 callback);
289 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
290 Bind(&ValidateFoldersOnFileThread,
291 file_paths,
292 validate_folders_callback));
293 }
294
295 void DevToolsFileHelper::AddValidatedFileSystem(
296 const AddFileSystemCallback& callback,
297 const std::vector<FilePath>& permitted_paths) {
298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
299 if (permitted_paths.empty()) {
300 std::string magic_file_name = FilePath(kMagicFileName).AsUTF8Unsafe();
301 std::string error_string = l10n_util::GetStringFUTF8(
302 IDS_DEV_TOOLS_MAGIC_FILE_NOT_EXISTS_MESSAGE,
303 UTF8ToUTF16(magic_file_name));
304 callback.Run(error_string, FileSystem());
305 return;
306 }
307 FilePath path = permitted_paths.at(0);
308 std::string registered_name;
309 std::string file_system_id = RegisterFileSystem(web_contents_,
310 path,
311 &registered_name);
312 std::string file_system_path = path.AsUTF8Unsafe();
313
314 DictionaryPrefUpdate update(profile_->GetPrefs(),
315 prefs::kDevToolsFileSystemPaths);
316 DictionaryValue* file_systems_paths_value = update.Get();
317 file_systems_paths_value->Set(file_system_path, Value::CreateNullValue());
318
319 FileSystem filesystem(file_system_id, registered_name, file_system_path);
320 callback.Run("", filesystem);
321 }
322
323 void DevToolsFileHelper::RequestFileSystems(
324 const RequestFileSystemsCallback& callback) {
325 const DictionaryValue* file_systems_paths_value =
326 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
327 std::vector<FilePath> saved_paths;
328 DictionaryValue::key_iterator it = file_systems_paths_value->begin_keys();
329 for (; it != file_systems_paths_value->end_keys(); ++it) {
330 std::string file_system_path = *it;
331 FilePath path = FilePath::FromUTF8Unsafe(file_system_path);
332 saved_paths.push_back(path);
333 }
334
335 ValidateFoldersCallback validate_folders_callback = Bind(
336 &DevToolsFileHelper::RestoreValidatedFileSystems,
337 weak_factory_.GetWeakPtr(),
338 callback);
339 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
340 Bind(&ValidateFoldersOnFileThread,
341 saved_paths,
342 validate_folders_callback));
343 }
344
345 void DevToolsFileHelper::RestoreValidatedFileSystems(
346 const RequestFileSystemsCallback& callback,
347 const std::vector<FilePath>& permitted_paths) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349 std::vector<FileSystem> file_systems;
350 std::vector<FilePath>::const_iterator it;
351 for (it = permitted_paths.begin(); it != permitted_paths.end(); ++it) {
352 std::string registered_name;
353 std::string file_system_id = RegisterFileSystem(web_contents_,
Tom Sepez 2013/01/11 18:34:17 Why is OK to skip the magic file check here?
354 *it,
355 &registered_name);
356 std::string file_system_path = it->AsUTF8Unsafe();
357 FileSystem filesystem(file_system_id, registered_name, file_system_path);
358 file_systems.push_back(filesystem);
359 }
360 callback.Run(file_systems);
361 }
362
363 void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 FilePath path = FilePath::FromUTF8Unsafe(file_system_path);
366 isolated_context()->RevokeFileSystemByPath(path);
kinuko 2013/01/10 07:33:58 Why do we return file_system_path to the inspector
vsevik 2013/01/10 08:14:21 I understand your concern. We need that because we
kinuko 2013/01/10 11:00:52 I see, thanks for your explanation! I think I'm cl
367
368 DictionaryPrefUpdate update(profile_->GetPrefs(),
369 prefs::kDevToolsFileSystemPaths);
370 DictionaryValue* file_systems_paths_value = update.Get();
371 file_systems_paths_value->RemoveWithoutPathExpansion(file_system_path, NULL);
372 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698