• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/devtools/devtools_file_helper.h"
6 
7 #include <set>
8 #include <vector>
9 
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"
40 
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;
49 
50 namespace {
51 
52 base::LazyInstance<base::FilePath>::Leaky
53     g_last_save_path = LAZY_INSTANCE_INITIALIZER;
54 
55 }  // namespace
56 
57 namespace {
58 
59 typedef Callback<void(const base::FilePath&)> SelectedCallback;
60 typedef Callback<void(void)> CanceledCallback;
61 
62 class SelectFileDialog : public ui::SelectFileDialog::Listener,
63                          public base::RefCounted<SelectFileDialog> {
64  public:
SelectFileDialog(const SelectedCallback & selected_callback,const CanceledCallback & canceled_callback,WebContents * web_contents)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));
73   }
74 
Show(ui::SelectFileDialog::Type type,const base::FilePath & default_path)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,
83       0,
84       base::FilePath::StringType(),
85       platform_util::GetTopLevel(web_contents_->GetNativeView()),
86       NULL);
87   }
88 
89   // ui::SelectFileDialog::Listener implementation.
FileSelected(const base::FilePath & path,int index,void * params)90   virtual void FileSelected(const base::FilePath& path,
91                             int index,
92                             void* params) OVERRIDE {
93     selected_callback_.Run(path);
94     Release();  // Balanced in ::Show.
95   }
96 
MultiFilesSelected(const std::vector<base::FilePath> & files,void * params)97   virtual 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";
101   }
102 
FileSelectionCanceled(void * params)103   virtual void FileSelectionCanceled(void* params) OVERRIDE {
104     canceled_callback_.Run();
105     Release();  // Balanced in ::Show.
106   }
107 
108  private:
109   friend class base::RefCounted<SelectFileDialog>;
~SelectFileDialog()110   virtual ~SelectFileDialog() {}
111 
112   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
113   SelectedCallback selected_callback_;
114   CanceledCallback canceled_callback_;
115   WebContents* web_contents_;
116 
117   DISALLOW_COPY_AND_ASSIGN(SelectFileDialog);
118 };
119 
WriteToFile(const base::FilePath & path,const std::string & content)120 void WriteToFile(const base::FilePath& path, const std::string& content) {
121   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
122   DCHECK(!path.empty());
123 
124   base::WriteFile(path, content.c_str(), content.length());
125 }
126 
AppendToFile(const base::FilePath & path,const std::string & content)127 void AppendToFile(const base::FilePath& path, const std::string& content) {
128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
129   DCHECK(!path.empty());
130 
131   base::AppendToFile(path, content.c_str(), content.length());
132 }
133 
isolated_context()134 storage::IsolatedContext* isolated_context() {
135   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
136   storage::IsolatedContext* isolated_context =
137       storage::IsolatedContext::GetInstance();
138   DCHECK(isolated_context);
139   return isolated_context;
140 }
141 
RegisterFileSystem(WebContents * web_contents,const base::FilePath & path,std::string * registered_name)142 std::string RegisterFileSystem(WebContents* web_contents,
143                                const base::FilePath& path,
144                                std::string* registered_name) {
145   DCHECK(BrowserThread::CurrentlyOn(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);
152 
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);
161 
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);
167 
168   return file_system_id;
169 }
170 
CreateFileSystemStruct(WebContents * web_contents,const std::string & file_system_id,const std::string & registered_name,const std::string & file_system_path)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);
184 }
185 
GetAddedFileSystemPaths(Profile * profile)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());
193   }
194   return result;
195 }
196 
197 }  // namespace
198 
FileSystem()199 DevToolsFileHelper::FileSystem::FileSystem() {
200 }
201 
FileSystem(const std::string & file_system_name,const std::string & root_url,const std::string & file_system_path)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) {
208 }
209 
DevToolsFileHelper(WebContents * web_contents,Profile * profile)210 DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents,
211                                        Profile* profile)
212     : web_contents_(web_contents),
213       profile_(profile),
214       weak_factory_(this) {
215 }
216 
~DevToolsFileHelper()217 DevToolsFileHelper::~DevToolsFileHelper() {
218 }
219 
Save(const std::string & url,const std::string & content,bool save_as,const SaveCallback & saveCallback,const SaveCallback & cancelCallback)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;
229   }
230 
231   const base::DictionaryValue* file_map =
232       profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles);
233   base::FilePath initial_path;
234 
235   const base::Value* path_value;
236   if (file_map->Get(base::MD5String(url), &path_value))
237     base::GetValueAsFilePath(*path_value, &initial_path);
238 
239   if (initial_path.empty()) {
240     GURL gurl(url);
241     std::string suggested_file_name = gurl.is_valid() ?
242         gurl.ExtractFileName() : url;
243 
244     if (suggested_file_name.length() > 64)
245       suggested_file_name = suggested_file_name.substr(0, 64);
246 
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);
254     }
255   }
256 
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);
269 }
270 
Append(const std::string & url,const std::string & content,const AppendCallback & callback)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));
280 }
281 
SaveAsFileSelected(const std::string & url,const std::string & content,const SaveCallback & callback,const base::FilePath & path)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;
288 
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));
297 }
298 
SaveAsFileSelectionCanceled(const SaveCallback & callback)299 void DevToolsFileHelper::SaveAsFileSelectionCanceled(
300     const SaveCallback& callback) {
301   callback.Run();
302 }
303 
AddFileSystem(const AddFileSystemCallback & callback,const ShowInfoBarCallback & show_info_bar_callback)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());
316 }
317 
UpgradeDraggedFileSystemPermissions(const std::string & file_system_url,const AddFileSystemCallback & callback,const ShowInfoBarCallback & show_info_bar_callback)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;
327   }
328 
329   std::vector<storage::MountPoints::MountPointInfo> mount_points;
330   isolated_context()->GetDraggedFileInfo(root_url.filesystem_id(),
331                                          &mount_points);
332 
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);
337 }
338 
InnerAddFileSystem(const AddFileSystemCallback & callback,const ShowInfoBarCallback & show_info_bar_callback,const base::FilePath & 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();
344 
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;
350   }
351 
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));
361 }
362 
AddUserConfirmedFileSystem(const AddFileSystemCallback & callback,const base::FilePath & path,bool allowed)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;
370   }
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();
376 
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());
382 
383   FileSystem filesystem = CreateFileSystemStruct(web_contents_,
384                                                  file_system_id,
385                                                  registered_name,
386                                                  file_system_path);
387   callback.Run(filesystem);
388 }
389 
RequestFileSystems(const RequestFileSystemsCallback & callback)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);
398 
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);
408   }
409   callback.Run(file_systems);
410 }
411 
RemoveFileSystem(const std::string & file_system_path)412 void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
413   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414   base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
415   isolated_context()->RevokeFileSystemByPath(path);
416 
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);
421 }
422 
IsFileSystemAdded(const std::string & file_system_path)423 bool DevToolsFileHelper::IsFileSystemAdded(
424     const std::string& file_system_path) {
425   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426   set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
427   return file_system_paths.find(file_system_path) != file_system_paths.end();
428 }
429