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" |
| 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[] = | |
| 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, | |
| 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); | |
|
kinuko
2013/01/09 15:17:50
I'm afraid I don't fully understand this 'kMagicFi
vsevik
2013/01/09 17:13:17
The directory will become writable only if the cor
kinuko
2013/01/10 07:33:58
Who creates the magic file? Sorry if I'm asking so
vsevik
2013/01/10 08:14:21
User should create a magic file in order to add a
| |
| 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 weak_factory_(this) { | |
|
kinuko
2013/01/09 15:17:50
I think you need to use ALLOW_THIS_IN_INITIALIZER_
vsevik
2013/01/10 07:01:16
Done.
| |
| 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); | |
|
kinuko
2013/01/09 15:17:50
nit: indent is off by 1 char? (line 310-311)
vsevik
2013/01/10 07:01:16
Done.
| |
| 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_, | |
| 354 *it, | |
| 355 ®istered_name); | |
|
kinuko
2013/01/09 15:17:50
nit: indent? (line 354-355)
vsevik
2013/01/10 07:01:16
Done.
| |
| 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); | |
| 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 |