OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 ®istered_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 ®istered_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 } | |
OLD | NEW |