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.
5 #include "chrome/browser/download/save_package_file_picker.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 "chrome/grit/generated_resources.h"
22 #include "content/public/browser/download_manager.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/save_page_type.h"
25 #include "content/public/browser/web_contents.h"
26 #include "ui/base/l10n/l10n_util.h"
28 #if defined(OS_CHROMEOS)
29 #include "chrome/browser/chromeos/drive/download_handler.h"
30 #include "chrome/browser/chromeos/drive/file_system_util.h"
33 using content::RenderProcessHost
;
34 using content::SavePageType
;
35 using content::WebContents
;
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;
43 void OnSavePackageDownloadCreated(content::DownloadItem
* download
) {
44 ChromeDownloadManagerDelegate::DisableSafeBrowsing(download
);
47 #if defined(OS_CHROMEOS)
48 void OnSavePackageDownloadCreatedChromeOS(
50 const base::FilePath
& drive_path
,
51 content::DownloadItem
* download
) {
52 drive::DownloadHandler::GetForProfile(profile
)->SetDownloadParams(
53 drive_path
, download
);
54 OnSavePackageDownloadCreated(download
);
57 // Trampoline callback between SubstituteDriveDownloadPath() and |callback|.
58 void ContinueSettingUpDriveDownload(
59 const content::SavePackagePathPickedCallback
& callback
,
60 content::SavePageType save_type
,
62 const base::FilePath
& drive_path
,
63 const base::FilePath
& drive_tmp_download_path
) {
64 if (drive_tmp_download_path
.empty()) // Substitution failed.
66 callback
.Run(drive_tmp_download_path
, save_type
, base::Bind(
67 &OnSavePackageDownloadCreatedChromeOS
, profile
, drive_path
));
71 // Adds "Webpage, HTML Only" type to FileTypeInfo.
72 void AddHtmlOnlyFileTypeInfo(
73 ui::SelectFileDialog::FileTypeInfo
* file_type_info
,
74 const base::FilePath::StringType
& extra_extension
) {
75 file_type_info
->extension_description_overrides
.push_back(
76 l10n_util::GetStringUTF16(IDS_SAVE_PAGE_DESC_HTML_ONLY
));
78 std::vector
<base::FilePath::StringType
> extensions
;
79 extensions
.push_back(FILE_PATH_LITERAL("html"));
80 extensions
.push_back(FILE_PATH_LITERAL("htm"));
81 if (!extra_extension
.empty())
82 extensions
.push_back(extra_extension
);
83 file_type_info
->extensions
.push_back(extensions
);
86 // Adds "Web Archive, Single File" type to FileTypeInfo.
87 void AddSingleFileFileTypeInfo(
88 ui::SelectFileDialog::FileTypeInfo
* file_type_info
) {
89 file_type_info
->extension_description_overrides
.push_back(
90 l10n_util::GetStringUTF16(IDS_SAVE_PAGE_DESC_SINGLE_FILE
));
92 std::vector
<base::FilePath::StringType
> extensions
;
93 extensions
.push_back(FILE_PATH_LITERAL("mhtml"));
94 file_type_info
->extensions
.push_back(extensions
);
97 // Chrome OS doesn't support HTML-Complete. crbug.com/154823
98 #if !defined(OS_CHROMEOS)
99 // Adds "Webpage, Complete" type to FileTypeInfo.
100 void AddCompleteFileTypeInfo(
101 ui::SelectFileDialog::FileTypeInfo
* file_type_info
,
102 const base::FilePath::StringType
& extra_extension
) {
103 file_type_info
->extension_description_overrides
.push_back(
104 l10n_util::GetStringUTF16(IDS_SAVE_PAGE_DESC_COMPLETE
));
106 std::vector
<base::FilePath::StringType
> extensions
;
107 extensions
.push_back(FILE_PATH_LITERAL("htm"));
108 extensions
.push_back(FILE_PATH_LITERAL("html"));
109 if (!extra_extension
.empty())
110 extensions
.push_back(extra_extension
);
111 file_type_info
->extensions
.push_back(extensions
);
115 } // anonymous namespace
117 bool SavePackageFilePicker::ShouldSaveAsMHTML() const {
118 #if !defined(OS_CHROMEOS)
119 if (!CommandLine::ForCurrentProcess()->HasSwitch(
120 switches::kSavePageAsMHTML
))
123 return can_save_as_complete_
;
126 SavePackageFilePicker::SavePackageFilePicker(
127 content::WebContents
* web_contents
,
128 const base::FilePath
& suggested_path
,
129 const base::FilePath::StringType
& default_extension
,
130 bool can_save_as_complete
,
131 DownloadPrefs
* download_prefs
,
132 const content::SavePackagePathPickedCallback
& callback
)
133 : render_process_id_(web_contents
->GetRenderProcessHost()->GetID()),
134 can_save_as_complete_(can_save_as_complete
),
135 download_prefs_(download_prefs
),
136 callback_(callback
) {
137 base::FilePath suggested_path_copy
= suggested_path
;
138 base::FilePath::StringType default_extension_copy
= default_extension
;
139 int file_type_index
= 0;
140 ui::SelectFileDialog::FileTypeInfo file_type_info
;
142 file_type_info
.support_drive
= true;
144 if (can_save_as_complete_
) {
145 // The option index is not zero-based. Put a dummy entry.
146 save_types_
.push_back(content::SAVE_PAGE_TYPE_UNKNOWN
);
148 base::FilePath::StringType extra_extension
;
149 if (ShouldSaveAsMHTML()) {
150 default_extension_copy
= FILE_PATH_LITERAL("mhtml");
151 suggested_path_copy
= suggested_path_copy
.ReplaceExtension(
152 default_extension_copy
);
154 if (!suggested_path_copy
.FinalExtension().empty() &&
155 !suggested_path_copy
.MatchesExtension(FILE_PATH_LITERAL(".htm")) &&
156 !suggested_path_copy
.MatchesExtension(FILE_PATH_LITERAL(".html"))) {
157 extra_extension
= suggested_path_copy
.FinalExtension().substr(1);
161 AddHtmlOnlyFileTypeInfo(&file_type_info
, extra_extension
);
162 save_types_
.push_back(content::SAVE_PAGE_TYPE_AS_ONLY_HTML
);
164 if (ShouldSaveAsMHTML()) {
165 AddSingleFileFileTypeInfo(&file_type_info
);
166 save_types_
.push_back(content::SAVE_PAGE_TYPE_AS_MHTML
);
169 #if !defined(OS_CHROMEOS)
170 AddCompleteFileTypeInfo(&file_type_info
, extra_extension
);
171 save_types_
.push_back(content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML
);
174 file_type_info
.include_all_files
= false;
176 content::SavePageType preferred_save_type
=
177 static_cast<content::SavePageType
>(download_prefs_
->save_file_type());
178 if (ShouldSaveAsMHTML())
179 preferred_save_type
= content::SAVE_PAGE_TYPE_AS_MHTML
;
181 // Select the item saved in the pref.
182 for (size_t i
= 0; i
< save_types_
.size(); ++i
) {
183 if (save_types_
[i
] == preferred_save_type
) {
189 // If the item saved in the pref was not found, use the last item.
190 if (!file_type_index
)
191 file_type_index
= save_types_
.size() - 1;
193 // The contents can not be saved as complete-HTML, so do not show the file
195 file_type_info
.extensions
.resize(1);
196 file_type_info
.extensions
[0].push_back(
197 suggested_path_copy
.FinalExtension());
199 if (!file_type_info
.extensions
[0][0].empty()) {
201 file_type_info
.extensions
[0][0].erase(0, 1);
204 file_type_info
.include_all_files
= true;
208 if (g_should_prompt_for_filename
) {
209 select_file_dialog_
= ui::SelectFileDialog::Create(
210 this, new ChromeSelectFilePolicy(web_contents
));
211 select_file_dialog_
->SelectFile(
212 ui::SelectFileDialog::SELECT_SAVEAS_FILE
,
217 default_extension_copy
,
218 platform_util::GetTopLevel(web_contents
->GetNativeView()),
221 // Just use 'suggested_path_copy' instead of opening the dialog prompt.
222 // Go through FileSelected() for consistency.
223 FileSelected(suggested_path_copy
, file_type_index
, NULL
);
227 SavePackageFilePicker::~SavePackageFilePicker() {
230 void SavePackageFilePicker::SetShouldPromptUser(bool should_prompt
) {
231 g_should_prompt_for_filename
= should_prompt
;
234 void SavePackageFilePicker::FileSelected(
235 const base::FilePath
& path
, int index
, void* unused_params
) {
236 scoped_ptr
<SavePackageFilePicker
> delete_this(this);
237 RenderProcessHost
* process
= RenderProcessHost::FromID(render_process_id_
);
240 SavePageType save_type
= content::SAVE_PAGE_TYPE_UNKNOWN
;
242 if (can_save_as_complete_
) {
243 DCHECK_LT(index
, static_cast<int>(save_types_
.size()));
244 save_type
= save_types_
[index
];
245 if (select_file_dialog_
.get() &&
246 select_file_dialog_
->HasMultipleFileTypeChoices())
247 download_prefs_
->SetSaveFileType(save_type
);
249 UMA_HISTOGRAM_ENUMERATION("Download.SavePageType",
251 content::SAVE_PAGE_TYPE_MAX
);
253 // Use "HTML Only" type as a dummy.
254 save_type
= content::SAVE_PAGE_TYPE_AS_ONLY_HTML
;
257 base::FilePath
path_copy(path
);
258 base::i18n::NormalizeFileNameEncoding(&path_copy
);
260 download_prefs_
->SetSaveFilePath(path_copy
.DirName());
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
,
283 callback_
.Run(path_copy
, save_type
,
284 base::Bind(&OnSavePackageDownloadCreated
));
287 void SavePackageFilePicker::FileSelectionCanceled(void* unused_params
) {