Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / devtools / devtools_file_helper.cc
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/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 "content/public/browser/browser_context.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/child_process_security_policy.h"
28 #include "content/public/browser/download_manager.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_view.h"
33 #include "content/public/common/content_client.h"
34 #include "content/public/common/url_constants.h"
35 #include "grit/generated_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/shell_dialogs/select_file_dialog.h"
38 #include "webkit/browser/fileapi/file_system_url.h"
39 #include "webkit/browser/fileapi/isolated_context.h"
40 #include "webkit/common/fileapi/file_system_util.h"
41
42 using base::Bind;
43 using base::Callback;
44 using content::BrowserContext;
45 using content::BrowserThread;
46 using content::DownloadManager;
47 using content::RenderViewHost;
48 using content::WebContents;
49 using std::set;
50
51 namespace {
52
53 base::LazyInstance<base::FilePath>::Leaky
54     g_last_save_path = LAZY_INSTANCE_INITIALIZER;
55
56 }  // namespace
57
58 namespace {
59
60 typedef Callback<void(const base::FilePath&)> SelectedCallback;
61 typedef Callback<void(void)> CanceledCallback;
62
63 class SelectFileDialog : public ui::SelectFileDialog::Listener,
64                          public base::RefCounted<SelectFileDialog> {
65  public:
66   SelectFileDialog(const SelectedCallback& selected_callback,
67                    const CanceledCallback& canceled_callback,
68                    WebContents* web_contents)
69       : selected_callback_(selected_callback),
70         canceled_callback_(canceled_callback),
71         web_contents_(web_contents) {
72     select_file_dialog_ = ui::SelectFileDialog::Create(
73         this, new ChromeSelectFilePolicy(web_contents));
74   }
75
76   void Show(ui::SelectFileDialog::Type type,
77             const base::FilePath& default_path) {
78     AddRef();  // Balanced in the three listener outcomes.
79     select_file_dialog_->SelectFile(
80       type,
81       base::string16(),
82       default_path,
83       NULL,
84       0,
85       base::FilePath::StringType(),
86       platform_util::GetTopLevel(web_contents_->GetView()->GetNativeView()),
87       NULL);
88   }
89
90   // ui::SelectFileDialog::Listener implementation.
91   virtual void FileSelected(const base::FilePath& path,
92                             int index,
93                             void* params) OVERRIDE {
94     selected_callback_.Run(path);
95     Release();  // Balanced in ::Show.
96   }
97
98   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
99                                   void* params) OVERRIDE {
100     Release();  // Balanced in ::Show.
101     NOTREACHED() << "Should not be able to select multiple files";
102   }
103
104   virtual void FileSelectionCanceled(void* params) OVERRIDE {
105     canceled_callback_.Run();
106     Release();  // Balanced in ::Show.
107   }
108
109  private:
110   friend class base::RefCounted<SelectFileDialog>;
111   virtual ~SelectFileDialog() {}
112
113   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
114   SelectedCallback selected_callback_;
115   CanceledCallback canceled_callback_;
116   WebContents* web_contents_;
117
118   DISALLOW_COPY_AND_ASSIGN(SelectFileDialog);
119 };
120
121 void WriteToFile(const base::FilePath& path, const std::string& content) {
122   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
123   DCHECK(!path.empty());
124
125   file_util::WriteFile(path, content.c_str(), content.length());
126 }
127
128 void AppendToFile(const base::FilePath& path, const std::string& content) {
129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
130   DCHECK(!path.empty());
131
132   file_util::AppendToFile(path, content.c_str(), content.length());
133 }
134
135 fileapi::IsolatedContext* isolated_context() {
136   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137   fileapi::IsolatedContext* isolated_context =
138       fileapi::IsolatedContext::GetInstance();
139   DCHECK(isolated_context);
140   return isolated_context;
141 }
142
143 std::string RegisterFileSystem(WebContents* web_contents,
144                                const base::FilePath& path,
145                                std::string* registered_name) {
146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147   CHECK(web_contents->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
148   std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
149       fileapi::kFileSystemTypeNativeLocal, path, registered_name);
150
151   content::ChildProcessSecurityPolicy* policy =
152       content::ChildProcessSecurityPolicy::GetInstance();
153   RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
154   int renderer_id = render_view_host->GetProcess()->GetID();
155   policy->GrantReadFileSystem(renderer_id, file_system_id);
156   policy->GrantWriteFileSystem(renderer_id, file_system_id);
157   policy->GrantCreateFileForFileSystem(renderer_id, file_system_id);
158   policy->GrantDeleteFromFileSystem(renderer_id, file_system_id);
159
160   // We only need file level access for reading FileEntries. Saving FileEntries
161   // just needs the file system to have read/write access, which is granted
162   // above if required.
163   if (!policy->CanReadFile(renderer_id, path))
164     policy->GrantReadFile(renderer_id, path);
165
166   return file_system_id;
167 }
168
169 DevToolsFileHelper::FileSystem CreateFileSystemStruct(
170     WebContents* web_contents,
171     const std::string& file_system_id,
172     const std::string& registered_name,
173     const std::string& file_system_path) {
174   const GURL origin = web_contents->GetURL().GetOrigin();
175   std::string file_system_name = fileapi::GetIsolatedFileSystemName(
176       origin,
177       file_system_id);
178   std::string root_url = fileapi::GetIsolatedFileSystemRootURIString(
179       origin,
180       file_system_id,
181       registered_name);
182   return DevToolsFileHelper::FileSystem(file_system_name,
183                                         root_url,
184                                         file_system_path);
185 }
186
187 set<std::string> GetAddedFileSystemPaths(Profile* profile) {
188   const base::DictionaryValue* file_systems_paths_value =
189       profile->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
190   set<std::string> result;
191   for (base::DictionaryValue::Iterator it(*file_systems_paths_value);
192        !it.IsAtEnd(); it.Advance()) {
193     result.insert(it.key());
194   }
195   return result;
196 }
197
198 }  // namespace
199
200 DevToolsFileHelper::FileSystem::FileSystem() {
201 }
202
203 DevToolsFileHelper::FileSystem::FileSystem(const std::string& file_system_name,
204                                            const std::string& root_url,
205                                            const std::string& file_system_path)
206     : file_system_name(file_system_name),
207       root_url(root_url),
208       file_system_path(file_system_path) {
209 }
210
211 DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents,
212                                        Profile* profile)
213     : web_contents_(web_contents),
214       profile_(profile),
215       weak_factory_(this) {
216 }
217
218 DevToolsFileHelper::~DevToolsFileHelper() {
219 }
220
221 void DevToolsFileHelper::Save(const std::string& url,
222                               const std::string& content,
223                               bool save_as,
224                               const SaveCallback& saveCallback,
225                               const SaveCallback& cancelCallback) {
226   PathsMap::iterator it = saved_files_.find(url);
227   if (it != saved_files_.end() && !save_as) {
228     SaveAsFileSelected(url, content, saveCallback, it->second);
229     return;
230   }
231
232   const base::DictionaryValue* file_map =
233       profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles);
234   base::FilePath initial_path;
235
236   const base::Value* path_value;
237   if (file_map->Get(base::MD5String(url), &path_value))
238     base::GetValueAsFilePath(*path_value, &initial_path);
239
240   if (initial_path.empty()) {
241     GURL gurl(url);
242     std::string suggested_file_name = gurl.is_valid() ?
243         gurl.ExtractFileName() : url;
244
245     if (suggested_file_name.length() > 64)
246       suggested_file_name = suggested_file_name.substr(0, 64);
247
248     if (!g_last_save_path.Pointer()->empty()) {
249       initial_path = g_last_save_path.Pointer()->DirName().AppendASCII(
250           suggested_file_name);
251     } else {
252       base::FilePath download_path = DownloadPrefs::FromDownloadManager(
253           BrowserContext::GetDownloadManager(profile_))->DownloadPath();
254       initial_path = download_path.AppendASCII(suggested_file_name);
255     }
256   }
257
258   scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
259       Bind(&DevToolsFileHelper::SaveAsFileSelected,
260            weak_factory_.GetWeakPtr(),
261            url,
262            content,
263            saveCallback),
264       Bind(&DevToolsFileHelper::SaveAsFileSelectionCanceled,
265            weak_factory_.GetWeakPtr(),
266            cancelCallback),
267       web_contents_);
268   select_file_dialog->Show(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
269                            initial_path);
270 }
271
272 void DevToolsFileHelper::Append(const std::string& url,
273                                 const std::string& content,
274                                 const AppendCallback& callback) {
275   PathsMap::iterator it = saved_files_.find(url);
276   if (it == saved_files_.end())
277     return;
278   callback.Run();
279   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
280                           Bind(&AppendToFile, it->second, content));
281 }
282
283 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
284                                             const std::string& content,
285                                             const SaveCallback& callback,
286                                             const base::FilePath& path) {
287   *g_last_save_path.Pointer() = path;
288   saved_files_[url] = path;
289
290   DictionaryPrefUpdate update(profile_->GetPrefs(),
291                               prefs::kDevToolsEditedFiles);
292   base::DictionaryValue* files_map = update.Get();
293   files_map->SetWithoutPathExpansion(base::MD5String(url),
294                                      base::CreateFilePathValue(path));
295   callback.Run();
296   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
297                           Bind(&WriteToFile, path, content));
298 }
299
300 void DevToolsFileHelper::SaveAsFileSelectionCanceled(
301     const SaveCallback& callback) {
302   callback.Run();
303 }
304
305 void DevToolsFileHelper::AddFileSystem(
306     const AddFileSystemCallback& callback,
307     const ShowInfoBarCallback& show_info_bar_callback) {
308   scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
309       Bind(&DevToolsFileHelper::InnerAddFileSystem,
310            weak_factory_.GetWeakPtr(),
311            callback,
312            show_info_bar_callback),
313       Bind(callback, FileSystem()),
314       web_contents_);
315   select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER,
316                            base::FilePath());
317 }
318
319 void DevToolsFileHelper::UpgradeDraggedFileSystemPermissions(
320     const std::string& file_system_url,
321     const AddFileSystemCallback& callback,
322     const ShowInfoBarCallback& show_info_bar_callback) {
323   fileapi::FileSystemURL root_url =
324       isolated_context()->CrackURL(GURL(file_system_url));
325   if (!root_url.is_valid() || !root_url.path().empty()) {
326     callback.Run(FileSystem());
327     return;
328   }
329
330   std::vector<fileapi::MountPoints::MountPointInfo> mount_points;
331   isolated_context()->GetDraggedFileInfo(root_url.filesystem_id(),
332                                          &mount_points);
333
334   std::vector<fileapi::MountPoints::MountPointInfo>::const_iterator it =
335       mount_points.begin();
336   for (; it != mount_points.end(); ++it)
337     InnerAddFileSystem(callback, show_info_bar_callback, it->path);
338 }
339
340 void DevToolsFileHelper::InnerAddFileSystem(
341     const AddFileSystemCallback& callback,
342     const ShowInfoBarCallback& show_info_bar_callback,
343     const base::FilePath& path) {
344   std::string file_system_path = path.AsUTF8Unsafe();
345
346   const base::DictionaryValue* file_systems_paths_value =
347       profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
348   if (file_systems_paths_value->HasKey(file_system_path)) {
349     callback.Run(FileSystem());
350     return;
351   }
352
353   std::string path_display_name = path.AsEndingWithSeparator().AsUTF8Unsafe();
354   base::string16 message = l10n_util::GetStringFUTF16(
355       IDS_DEV_TOOLS_CONFIRM_ADD_FILE_SYSTEM_MESSAGE,
356       base::UTF8ToUTF16(path_display_name));
357   show_info_bar_callback.Run(
358       message,
359       Bind(&DevToolsFileHelper::AddUserConfirmedFileSystem,
360            weak_factory_.GetWeakPtr(),
361            callback, path));
362 }
363
364 void DevToolsFileHelper::AddUserConfirmedFileSystem(
365     const AddFileSystemCallback& callback,
366     const base::FilePath& path,
367     bool allowed) {
368   if (!allowed) {
369     callback.Run(FileSystem());
370     return;
371   }
372   std::string registered_name;
373   std::string file_system_id = RegisterFileSystem(web_contents_,
374                                                   path,
375                                                   &registered_name);
376   std::string file_system_path = path.AsUTF8Unsafe();
377
378   DictionaryPrefUpdate update(profile_->GetPrefs(),
379                               prefs::kDevToolsFileSystemPaths);
380   base::DictionaryValue* file_systems_paths_value = update.Get();
381   file_systems_paths_value->SetWithoutPathExpansion(
382       file_system_path, base::Value::CreateNullValue());
383
384   FileSystem filesystem = CreateFileSystemStruct(web_contents_,
385                                                  file_system_id,
386                                                  registered_name,
387                                                  file_system_path);
388   callback.Run(filesystem);
389 }
390
391 void DevToolsFileHelper::RequestFileSystems(
392     const RequestFileSystemsCallback& callback) {
393   set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
394   set<std::string>::const_iterator it = file_system_paths.begin();
395   std::vector<FileSystem> file_systems;
396   for (; it != file_system_paths.end(); ++it) {
397     std::string file_system_path = *it;
398     base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
399
400     std::string registered_name;
401     std::string file_system_id = RegisterFileSystem(web_contents_,
402                                                     path,
403                                                     &registered_name);
404     FileSystem filesystem = CreateFileSystemStruct(web_contents_,
405                                                    file_system_id,
406                                                    registered_name,
407                                                    file_system_path);
408     file_systems.push_back(filesystem);
409   }
410   callback.Run(file_systems);
411 }
412
413 void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
414   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415   base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
416   isolated_context()->RevokeFileSystemByPath(path);
417
418   DictionaryPrefUpdate update(profile_->GetPrefs(),
419                               prefs::kDevToolsFileSystemPaths);
420   base::DictionaryValue* file_systems_paths_value = update.Get();
421   file_systems_paths_value->RemoveWithoutPathExpansion(file_system_path, NULL);
422 }
423
424 bool DevToolsFileHelper::IsFileSystemAdded(
425     const std::string& file_system_path) {
426   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
427   set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
428   return file_system_paths.find(file_system_path) != file_system_paths.end();
429 }