Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / download / save_package_file_picker.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/download/save_package_file_picker.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/i18n/file_util_icu.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_member.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/download/chrome_download_manager_delegate.h"
15 #include "chrome/browser/download/download_prefs.h"
16 #include "chrome/browser/platform_util.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/chrome_select_file_policy.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/pref_names.h"
21 #include "content/public/browser/download_manager.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/save_page_type.h"
24 #include "content/public/browser/web_contents.h"
25 #include "grit/generated_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27
28 #if defined(OS_CHROMEOS)
29 #include "chrome/browser/chromeos/drive/download_handler.h"
30 #include "chrome/browser/chromeos/drive/file_system_util.h"
31 #endif
32
33 using content::RenderProcessHost;
34 using content::SavePageType;
35 using content::WebContents;
36
37 namespace {
38
39 // If false, we don't prompt the user as to where to save the file.  This
40 // exists only for testing.
41 bool g_should_prompt_for_filename = true;
42
43 #if !defined(OS_CHROMEOS)
44 // Used for mapping between SavePageType constants and the indexes above.
45 const SavePageType kIndexToSaveType[] = {
46   content::SAVE_PAGE_TYPE_UNKNOWN,
47   content::SAVE_PAGE_TYPE_AS_ONLY_HTML,
48   content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
49 };
50
51 int SavePackageTypeToIndex(SavePageType type) {
52   for (size_t i = 0; i < arraysize(kIndexToSaveType); ++i) {
53     if (kIndexToSaveType[i] == type)
54       return i;
55   }
56   NOTREACHED();
57   return -1;
58 }
59 #endif
60
61 // Indexes used for specifying which element in the extensions dropdown
62 // the user chooses when picking a save type.
63 const int kSelectFileHtmlOnlyIndex = 1;
64 const int kSelectFileCompleteIndex = 2;
65
66 // Used for mapping between the IDS_ string identifiers and the indexes above.
67 const int kIndexToIDS[] = {
68   0, IDS_SAVE_PAGE_DESC_HTML_ONLY, IDS_SAVE_PAGE_DESC_COMPLETE,
69 };
70
71 void OnSavePackageDownloadCreated(content::DownloadItem* download) {
72   ChromeDownloadManagerDelegate::DisableSafeBrowsing(download);
73 }
74
75 #if defined(OS_CHROMEOS)
76 void OnSavePackageDownloadCreatedChromeOS(
77     Profile* profile,
78     const base::FilePath& drive_path,
79     content::DownloadItem* download) {
80   drive::DownloadHandler::GetForProfile(profile)->SetDownloadParams(
81       drive_path, download);
82   OnSavePackageDownloadCreated(download);
83 }
84
85 // Trampoline callback between SubstituteDriveDownloadPath() and |callback|.
86 void ContinueSettingUpDriveDownload(
87     const content::SavePackagePathPickedCallback& callback,
88     content::SavePageType save_type,
89     Profile* profile,
90     const base::FilePath& drive_path,
91     const base::FilePath& drive_tmp_download_path) {
92   if (drive_tmp_download_path.empty())  // Substitution failed.
93     return;
94   callback.Run(drive_tmp_download_path, save_type, base::Bind(
95       &OnSavePackageDownloadCreatedChromeOS, profile, drive_path));
96 }
97 #endif
98
99 }  // anonymous namespace
100
101 bool SavePackageFilePicker::ShouldSaveAsMHTML() const {
102 #if !defined(OS_CHROMEOS)
103   if (!CommandLine::ForCurrentProcess()->HasSwitch(
104              switches::kSavePageAsMHTML))
105     return false;
106 #endif
107   return can_save_as_complete_;
108 }
109
110 SavePackageFilePicker::SavePackageFilePicker(
111     content::WebContents* web_contents,
112     const base::FilePath& suggested_path,
113     const base::FilePath::StringType& default_extension,
114     bool can_save_as_complete,
115     DownloadPrefs* download_prefs,
116     const content::SavePackagePathPickedCallback& callback)
117     : render_process_id_(web_contents->GetRenderProcessHost()->GetID()),
118       can_save_as_complete_(can_save_as_complete),
119       download_prefs_(download_prefs),
120       callback_(callback) {
121   base::FilePath suggested_path_copy = suggested_path;
122   base::FilePath::StringType default_extension_copy = default_extension;
123   int file_type_index = 0;
124   ui::SelectFileDialog::FileTypeInfo file_type_info;
125
126 #if defined(OS_CHROMEOS)
127   file_type_info.support_drive = true;
128 #else
129   file_type_index = SavePackageTypeToIndex(
130       static_cast<SavePageType>(download_prefs_->save_file_type()));
131   DCHECK_NE(-1, file_type_index);
132 #endif
133
134   // TODO(benjhayden): Merge the first branch with the second when all of the
135   // platform-specific file selection dialog implementations fully support
136   // switching save-as file formats, and remove the flag/switch.
137   if (ShouldSaveAsMHTML()) {
138     default_extension_copy = FILE_PATH_LITERAL("mhtml");
139     suggested_path_copy = suggested_path_copy.ReplaceExtension(
140         default_extension_copy);
141   } else if (can_save_as_complete_) {
142     // NOTE: this branch will never run on chromeos because ShouldSaveAsHTML()
143     // == can_save_as_complete_ on chromeos.
144     bool add_extra_extension = false;
145     base::FilePath::StringType extra_extension;
146     if (!suggested_path_copy.FinalExtension().empty() &&
147         !suggested_path_copy.MatchesExtension(FILE_PATH_LITERAL(".htm")) &&
148         !suggested_path_copy.MatchesExtension(FILE_PATH_LITERAL(".html"))) {
149       add_extra_extension = true;
150       extra_extension = suggested_path_copy.FinalExtension().substr(1);
151     }
152
153     static const size_t kNumberExtensions = arraysize(kIndexToIDS) - 1;
154     file_type_info.extensions.resize(kNumberExtensions);
155     file_type_info.extension_description_overrides.resize(kNumberExtensions);
156
157     // Indices into kIndexToIDS are 1-based whereas indices into
158     // file_type_info.extensions are 0-based. Hence the '-1's.
159     // If you switch these resize()/direct-assignment patterns to push_back(),
160     // then you risk breaking FileSelected()'s use of |index|.
161
162     file_type_info.extension_description_overrides[
163       kSelectFileHtmlOnlyIndex - 1] = l10n_util::GetStringUTF16(kIndexToIDS[
164           kSelectFileHtmlOnlyIndex]);
165     file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
166         FILE_PATH_LITERAL("htm"));
167     file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
168         FILE_PATH_LITERAL("html"));
169     if (add_extra_extension) {
170       file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
171           extra_extension);
172     }
173
174     file_type_info.extension_description_overrides[
175       kSelectFileCompleteIndex - 1] = l10n_util::GetStringUTF16(kIndexToIDS[
176           kSelectFileCompleteIndex]);
177     file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
178         FILE_PATH_LITERAL("htm"));
179     file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
180         FILE_PATH_LITERAL("html"));
181     if (add_extra_extension) {
182       file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
183           extra_extension);
184     }
185
186     file_type_info.include_all_files = false;
187   } else {
188     // The contents can not be saved as complete-HTML, so do not show the file
189     // filters.
190     file_type_info.extensions.resize(1);
191     file_type_info.extensions[0].push_back(
192         suggested_path_copy.FinalExtension());
193
194     if (!file_type_info.extensions[0][0].empty()) {
195       // Drop the .
196       file_type_info.extensions[0][0].erase(0, 1);
197     }
198
199     file_type_info.include_all_files = true;
200     file_type_index = 1;
201   }
202
203   if (g_should_prompt_for_filename) {
204     select_file_dialog_ = ui::SelectFileDialog::Create(
205         this, new ChromeSelectFilePolicy(web_contents));
206     select_file_dialog_->SelectFile(
207         ui::SelectFileDialog::SELECT_SAVEAS_FILE,
208         base::string16(),
209         suggested_path_copy,
210         &file_type_info,
211         file_type_index,
212         default_extension_copy,
213         platform_util::GetTopLevel(web_contents->GetNativeView()),
214         NULL);
215   } else {
216     // Just use 'suggested_path_copy' instead of opening the dialog prompt.
217     // Go through FileSelected() for consistency.
218     FileSelected(suggested_path_copy, file_type_index, NULL);
219   }
220 }
221
222 SavePackageFilePicker::~SavePackageFilePicker() {
223 }
224
225 void SavePackageFilePicker::SetShouldPromptUser(bool should_prompt) {
226   g_should_prompt_for_filename = should_prompt;
227 }
228
229 void SavePackageFilePicker::FileSelected(
230     const base::FilePath& path, int index, void* unused_params) {
231   scoped_ptr<SavePackageFilePicker> delete_this(this);
232   RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_);
233   if (!process)
234     return;
235   SavePageType save_type = content::SAVE_PAGE_TYPE_UNKNOWN;
236
237   if (ShouldSaveAsMHTML()) {
238     save_type = content::SAVE_PAGE_TYPE_AS_MHTML;
239   } else {
240 #if defined(OS_CHROMEOS)
241     save_type = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
242 #else
243     // The option index is not zero-based.
244     DCHECK(index >= kSelectFileHtmlOnlyIndex &&
245            index <= kSelectFileCompleteIndex);
246     save_type = kIndexToSaveType[index];
247     if (select_file_dialog_.get() &&
248         select_file_dialog_->HasMultipleFileTypeChoices())
249       download_prefs_->SetSaveFileType(save_type);
250 #endif
251   }
252
253   UMA_HISTOGRAM_ENUMERATION("Download.SavePageType",
254                             save_type,
255                             content::SAVE_PAGE_TYPE_MAX);
256
257   base::FilePath path_copy(path);
258   file_util::NormalizeFileNameEncoding(&path_copy);
259
260   download_prefs_->SetSaveFilePath(path_copy.DirName());
261
262 #if defined(OS_CHROMEOS)
263   if (drive::util::IsUnderDriveMountPoint(path_copy)) {
264     // Here's a map to the callback chain:
265     // SubstituteDriveDownloadPath ->
266     //   ContinueSettingUpDriveDownload ->
267     //     callback_ = SavePackage::OnPathPicked ->
268     //       download_created_callback = OnSavePackageDownloadCreatedChromeOS
269     Profile* profile = Profile::FromBrowserContext(
270         process->GetBrowserContext());
271     drive::DownloadHandler* drive_download_handler =
272         drive::DownloadHandler::GetForProfile(profile);
273     drive_download_handler->SubstituteDriveDownloadPath(
274         path_copy, NULL, base::Bind(&ContinueSettingUpDriveDownload,
275                                     callback_,
276                                     save_type,
277                                     profile,
278                                     path_copy));
279     return;
280   }
281 #endif
282
283   callback_.Run(path_copy, save_type,
284                 base::Bind(&OnSavePackageDownloadCreated));
285 }
286
287 void SavePackageFilePicker::FileSelectionCanceled(void* unused_params) {
288   delete this;
289 }