Chromium Code Reviews| 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" |
| 24 #include "content/public/browser/browser_thread.h" | |
| 25 #include "content/public/browser/child_process_security_policy.h" | |
| 23 #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 "grit/generated_resources.h" | |
| 24 #include "ui/base/dialogs/select_file_dialog.h" | 31 #include "ui/base/dialogs/select_file_dialog.h" |
| 32 #include "ui/base/l10n/l10n_util.h" | |
| 33 #include "webkit/fileapi/isolated_context.h" | |
| 25 | 34 |
| 26 using base::Bind; | 35 using base::Bind; |
| 27 using base::Callback; | 36 using base::Callback; |
| 28 using content::BrowserContext; | 37 using content::BrowserContext; |
| 29 using content::BrowserThread; | 38 using content::BrowserThread; |
| 30 using content::DownloadManager; | 39 using content::DownloadManager; |
| 40 using content::RenderViewHost; | |
| 41 using content::WebContents; | |
| 31 | 42 |
| 32 namespace { | 43 namespace { |
| 33 | 44 |
| 34 base::LazyInstance<FilePath>::Leaky | 45 base::LazyInstance<FilePath>::Leaky |
| 35 g_last_save_path = LAZY_INSTANCE_INITIALIZER; | 46 g_last_save_path = LAZY_INSTANCE_INITIALIZER; |
| 36 | 47 |
| 37 } // namespace | 48 } // namespace |
| 38 | 49 |
| 39 namespace { | 50 namespace { |
| 40 | 51 |
| 41 typedef Callback<void(const FilePath&)> SelectedCallback; | 52 typedef Callback<void(const FilePath&)> SelectedCallback; |
| 42 typedef Callback<void(void)> CanceledCallback; | 53 typedef Callback<void(void)> CanceledCallback; |
| 43 | 54 |
| 55 const char kMagicFileName[] = ".allow-devtools-edit"; | |
| 56 | |
| 44 class SelectFileDialog : public ui::SelectFileDialog::Listener, | 57 class SelectFileDialog : public ui::SelectFileDialog::Listener, |
| 45 public base::RefCounted<SelectFileDialog> { | 58 public base::RefCounted<SelectFileDialog> { |
| 46 public: | 59 public: |
| 47 SelectFileDialog(const SelectedCallback& selected_callback, | 60 SelectFileDialog(const SelectedCallback& selected_callback, |
| 48 const CanceledCallback& canceled_callback) | 61 const CanceledCallback& canceled_callback) |
| 49 : selected_callback_(selected_callback), | 62 : selected_callback_(selected_callback), |
| 50 canceled_callback_(canceled_callback) { | 63 canceled_callback_(canceled_callback) { |
| 51 select_file_dialog_ = ui::SelectFileDialog::Create( | 64 select_file_dialog_ = ui::SelectFileDialog::Create( |
| 52 this, new ChromeSelectFilePolicy(NULL)); | 65 this, new ChromeSelectFilePolicy(NULL)); |
| 53 } | 66 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 file_util::WriteFile(path, content.c_str(), content.length()); | 111 file_util::WriteFile(path, content.c_str(), content.length()); |
| 99 } | 112 } |
| 100 | 113 |
| 101 void AppendToFile(const FilePath& path, const std::string& content) { | 114 void AppendToFile(const FilePath& path, const std::string& content) { |
| 102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 103 DCHECK(!path.empty()); | 116 DCHECK(!path.empty()); |
| 104 | 117 |
| 105 file_util::AppendToFile(path, content.c_str(), content.length()); | 118 file_util::AppendToFile(path, content.c_str(), content.length()); |
| 106 } | 119 } |
| 107 | 120 |
| 121 fileapi::IsolatedContext* isolated_context() { | |
| 122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 123 fileapi::IsolatedContext* isolated_context = | |
| 124 fileapi::IsolatedContext::GetInstance(); | |
| 125 DCHECK(isolated_context); | |
| 126 return isolated_context; | |
| 127 } | |
| 128 | |
| 129 std::string RegisterFilesystem(WebContents* web_contents, | |
| 130 const FilePath& path, | |
| 131 std::string* registered_name) { | |
| 132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 133 std::string filesystem_id = isolated_context()->RegisterFileSystemForPath( | |
| 134 fileapi::kFileSystemTypeNativeLocal, path, registered_name); | |
| 135 | |
| 136 content::ChildProcessSecurityPolicy* policy = | |
| 137 content::ChildProcessSecurityPolicy::GetInstance(); | |
| 138 RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); | |
| 139 int renderer_id = render_view_host->GetProcess()->GetID(); | |
| 140 policy->GrantReadWriteFileSystem(renderer_id, filesystem_id); | |
| 141 | |
| 142 // We only need file level access for reading FileEntries. Saving FileEntries | |
| 143 // just needs the file system to have read/write access, which is granted | |
| 144 // above if required. | |
| 145 if (!policy->CanReadFile(renderer_id, path)) | |
| 146 policy->GrantReadFile(renderer_id, path); | |
| 147 | |
| 148 return filesystem_id; | |
| 149 } | |
| 150 | |
| 151 bool CheckSecurityFileExistsInFolder(const FilePath& path) { | |
| 152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
|
pfeldman
2012/12/28 15:09:58
inline
| |
| 153 FilePath security_file_path = path.Append(FILE_PATH_LITERAL(kMagicFileName)); | |
| 154 return file_util::PathExists(security_file_path); | |
| 155 } | |
| 156 | |
| 157 typedef Callback<void(const std::vector<FilePath>&)> CheckFoldersCallback; | |
| 158 | |
| 159 void FoldersCheckedOnFileThread(const CheckFoldersCallback& callback, | |
| 160 const std::vector<FilePath>& permitted_paths) { | |
| 161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 162 callback.Run(permitted_paths); | |
| 163 } | |
| 164 | |
| 165 void CheckFoldersOnFileThread(const std::vector<FilePath>& file_paths, | |
| 166 const CheckFoldersCallback& callback) { | |
| 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 168 std::vector<FilePath> permitted_paths; | |
| 169 std::vector<FilePath>::const_iterator it; | |
| 170 for (it = file_paths.begin(); it != file_paths.end(); ++it) { | |
| 171 if (CheckSecurityFileExistsInFolder(*it)) | |
| 172 permitted_paths.push_back(*it); | |
| 173 } | |
| 174 BrowserThread::PostTask( | |
| 175 BrowserThread::UI, FROM_HERE, | |
| 176 Bind(&FoldersCheckedOnFileThread, callback, permitted_paths)); | |
| 177 } | |
| 178 | |
| 179 void CheckFolders(const std::vector<FilePath>& file_paths, | |
| 180 const CheckFoldersCallback& callback) { | |
| 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 182 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 183 Bind(&CheckFoldersOnFileThread, | |
| 184 file_paths, | |
| 185 callback)); | |
| 186 } | |
| 187 | |
| 188 typedef Callback<void(const FilePath&, bool)> CheckFolderCallback; | |
| 189 | |
| 190 void FolderCheckedOnFileThread(const CheckFolderCallback& callback, | |
| 191 const FilePath& path, | |
| 192 bool permitted) { | |
| 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
|
pfeldman
2012/12/28 15:09:58
inline
| |
| 194 callback.Run(path, permitted); | |
| 195 } | |
| 196 | |
| 197 void CheckFolderOnFileThread(const FilePath& path, | |
| 198 const CheckFolderCallback& callback) { | |
| 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 200 bool permitted = CheckSecurityFileExistsInFolder(path); | |
| 201 BrowserThread::PostTask( | |
| 202 BrowserThread::UI, FROM_HERE, | |
| 203 Bind(&FolderCheckedOnFileThread, callback, path, permitted)); | |
| 204 } | |
| 205 | |
| 206 void CheckFolder(const FilePath& path, const CheckFolderCallback& callback) { | |
|
pfeldman
2012/12/28 15:09:58
Please inline it.
| |
| 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 208 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 209 Bind(&CheckFolderOnFileThread, path, callback)); | |
| 210 } | |
| 211 | |
| 108 } // namespace | 212 } // namespace |
| 109 | 213 |
| 110 DevToolsFileHelper::DevToolsFileHelper(Profile* profile) : profile_(profile), | 214 DevToolsFileHelper::Filesystem::Filesystem() { |
| 111 weak_factory_(this) { | 215 } |
| 216 | |
| 217 DevToolsFileHelper::Filesystem::Filesystem(const std::string& filesystem_id, | |
| 218 const std::string& registered_name, | |
| 219 const std::string& filesystem_path) | |
| 220 : filesystem_id(filesystem_id), | |
| 221 registered_name(registered_name), | |
| 222 filesystem_path(filesystem_path) { | |
| 223 } | |
| 224 | |
| 225 DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents, | |
| 226 Profile* profile) | |
| 227 : web_contents_(web_contents), profile_(profile), weak_factory_(this) { | |
| 112 } | 228 } |
| 113 | 229 |
| 114 DevToolsFileHelper::~DevToolsFileHelper() { | 230 DevToolsFileHelper::~DevToolsFileHelper() { |
| 115 } | 231 } |
| 116 | 232 |
| 117 void DevToolsFileHelper::Save(const std::string& url, | 233 void DevToolsFileHelper::Save(const std::string& url, |
| 118 const std::string& content, | 234 const std::string& content, |
| 119 bool save_as, | 235 bool save_as, |
| 120 const SaveCallback& callback) { | 236 const SaveCallback& callback) { |
| 121 PathsMap::iterator it = saved_files_.find(url); | 237 PathsMap::iterator it = saved_files_.find(url); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 163 } | 279 } |
| 164 | 280 |
| 165 void DevToolsFileHelper::Append(const std::string& url, | 281 void DevToolsFileHelper::Append(const std::string& url, |
| 166 const std::string& content, | 282 const std::string& content, |
| 167 const AppendCallback& callback) { | 283 const AppendCallback& callback) { |
| 168 PathsMap::iterator it = saved_files_.find(url); | 284 PathsMap::iterator it = saved_files_.find(url); |
| 169 if (it == saved_files_.end()) | 285 if (it == saved_files_.end()) |
| 170 return; | 286 return; |
| 171 callback.Run(); | 287 callback.Run(); |
| 172 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 288 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 173 base::Bind(&AppendToFile, it->second, content)); | 289 Bind(&AppendToFile, it->second, content)); |
| 174 } | 290 } |
| 175 | 291 |
| 176 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url, | 292 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url, |
| 177 const std::string& content, | 293 const std::string& content, |
| 178 const SaveCallback& callback, | 294 const SaveCallback& callback, |
| 179 const FilePath& path) { | 295 const FilePath& path) { |
| 180 *g_last_save_path.Pointer() = path; | 296 *g_last_save_path.Pointer() = path; |
| 181 saved_files_[url] = path; | 297 saved_files_[url] = path; |
| 182 | 298 |
| 183 DictionaryPrefUpdate update(profile_->GetPrefs(), | 299 DictionaryPrefUpdate update(profile_->GetPrefs(), |
| 184 prefs::kDevToolsEditedFiles); | 300 prefs::kDevToolsEditedFiles); |
| 185 DictionaryValue* files_map = update.Get(); | 301 DictionaryValue* files_map = update.Get(); |
| 186 files_map->SetWithoutPathExpansion(base::MD5String(url), | 302 files_map->SetWithoutPathExpansion(base::MD5String(url), |
| 187 base::CreateFilePathValue(path)); | 303 base::CreateFilePathValue(path)); |
| 188 callback.Run(); | 304 callback.Run(); |
| 189 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 305 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 190 base::Bind(&WriteToFile, path, content)); | 306 Bind(&WriteToFile, path, content)); |
| 191 } | 307 } |
| 192 | 308 |
| 193 void DevToolsFileHelper::SaveAsFileSelectionCanceled() { | 309 void DevToolsFileHelper::SaveAsFileSelectionCanceled() { |
| 194 } | 310 } |
| 311 | |
| 312 void DevToolsFileHelper::AddFilesystem(const AddFilesystemCallback& callback) { | |
| 313 scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog( | |
| 314 Bind(&DevToolsFileHelper::FolderSelected, | |
| 315 weak_factory_.GetWeakPtr(), | |
| 316 callback), | |
| 317 Bind(&DevToolsFileHelper::FolderSelectionCanceled, | |
| 318 weak_factory_.GetWeakPtr(), | |
| 319 callback)); | |
| 320 select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER, FilePath()); | |
| 321 } | |
| 322 | |
| 323 void DevToolsFileHelper::FolderSelected(const AddFilesystemCallback& callback, | |
| 324 const FilePath& path) { | |
| 325 CheckFolder(path, Bind(&DevToolsFileHelper::SelectedFilesystemChecked, | |
|
pfeldman
2012/12/28 15:09:58
CheckFolders(std::vector(1, path))
| |
| 326 weak_factory_.GetWeakPtr(), | |
| 327 callback)); | |
| 328 } | |
| 329 | |
| 330 void DevToolsFileHelper::SelectedFilesystemChecked( | |
| 331 const AddFilesystemCallback& callback, | |
| 332 const FilePath& path, | |
| 333 bool permitted) { | |
| 334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 335 if (!permitted) { | |
| 336 std::string error_string = l10n_util::GetStringFUTF8( | |
| 337 IDS_DEV_TOOLS_MAGIC_FILE_NOT_EXISTS_MESSAGE, | |
| 338 UTF8ToUTF16(kMagicFileName)); | |
| 339 callback.Run(error_string, Filesystem()); | |
| 340 return; | |
| 341 } | |
| 342 | |
| 343 std::string registered_name; | |
| 344 std::string filesystem_id = RegisterFilesystem(web_contents_, | |
| 345 path, | |
| 346 ®istered_name); | |
| 347 std::string filesystem_path = path.AsUTF8Unsafe(); | |
| 348 | |
| 349 DictionaryPrefUpdate update(profile_->GetPrefs(), | |
| 350 prefs::kDevToolsFilesystemPaths); | |
| 351 DictionaryValue* filesystems_paths_value = update.Get(); | |
| 352 filesystems_paths_value->Set(filesystem_path, Value::CreateNullValue()); | |
| 353 | |
| 354 Filesystem filesystem(filesystem_id, registered_name, filesystem_path); | |
| 355 callback.Run("", filesystem); | |
| 356 } | |
| 357 | |
| 358 void DevToolsFileHelper::FolderSelectionCanceled( | |
| 359 const AddFilesystemCallback& callback) { | |
| 360 callback.Run("", Filesystem()); | |
| 361 } | |
| 362 | |
| 363 void DevToolsFileHelper::RequestFilesystems( | |
| 364 const RequestFilesystemsCallback& callback) { | |
| 365 const DictionaryValue* filesystems_paths_value = | |
| 366 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFilesystemPaths); | |
| 367 std::vector<FilePath> saved_paths; | |
| 368 DictionaryValue::key_iterator it = filesystems_paths_value->begin_keys(); | |
| 369 for (; it != filesystems_paths_value->end_keys(); ++it) { | |
| 370 std::string filesystem_path = *it; | |
| 371 FilePath path = FilePath::FromUTF8Unsafe(filesystem_path); | |
| 372 saved_paths.push_back(path); | |
| 373 } | |
| 374 | |
| 375 CheckFolders(saved_paths, | |
| 376 Bind(&DevToolsFileHelper::RegisterPermittedFilesystems, | |
| 377 weak_factory_.GetWeakPtr(), | |
| 378 callback)); | |
| 379 } | |
| 380 | |
| 381 void DevToolsFileHelper::RegisterPermittedFilesystems( | |
| 382 const RequestFilesystemsCallback& callback, | |
| 383 const std::vector<FilePath>& permitted_paths) { | |
| 384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 385 std::vector<Filesystem> filesystems; | |
| 386 std::vector<FilePath>::const_iterator it; | |
| 387 for (it = permitted_paths.begin(); it != permitted_paths.end(); ++it) { | |
| 388 std::string registered_name; | |
| 389 std::string filesystem_id = RegisterFilesystem(web_contents_, | |
| 390 *it, | |
| 391 ®istered_name); | |
| 392 std::string filesystem_path = it->AsUTF8Unsafe(); | |
| 393 Filesystem filesystem(filesystem_id, registered_name, filesystem_path); | |
| 394 filesystems.push_back(filesystem); | |
| 395 } | |
| 396 callback.Run(filesystems); | |
| 397 } | |
| 398 | |
| 399 void DevToolsFileHelper::RemoveFilesystem(const std::string& filesystem_path) { | |
| 400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 401 FilePath path = FilePath::FromUTF8Unsafe(filesystem_path); | |
| 402 isolated_context()->RevokeFileSystemByPath(path); | |
| 403 | |
| 404 DictionaryPrefUpdate update(profile_->GetPrefs(), | |
| 405 prefs::kDevToolsFilesystemPaths); | |
| 406 DictionaryValue* filesystems_paths_value = update.Get(); | |
| 407 filesystems_paths_value->RemoveWithoutPathExpansion(filesystem_path, NULL); | |
| 408 } | |
| OLD | NEW |