| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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/download/save_package.h" | 5 #include "chrome/browser/download/save_package.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/path_service.h" | 10 #include "base/path_service.h" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "net/url_request/url_request_context.h" | 37 #include "net/url_request/url_request_context.h" |
| 38 #include "webkit/glue/dom_serializer_delegate.h" | 38 #include "webkit/glue/dom_serializer_delegate.h" |
| 39 | 39 |
| 40 #if defined(OS_WIN) | 40 #if defined(OS_WIN) |
| 41 #include "base/win_util.h" | 41 #include "base/win_util.h" |
| 42 #include "chrome/common/win_util.h" | 42 #include "chrome/common/win_util.h" |
| 43 #endif | 43 #endif |
| 44 | 44 |
| 45 using base::Time; | 45 using base::Time; |
| 46 | 46 |
| 47 // This structure is for storing parameters which we will use to create a |
| 48 // SavePackage object later. |
| 49 struct SavePackageParam { |
| 50 // MIME type of current tab contents. |
| 51 const std::string current_tab_mime_type; |
| 52 // Pointer to preference service. |
| 53 SavePackage::SavePackageType save_type; |
| 54 // File path for main html file. |
| 55 FilePath saved_main_file_path; |
| 56 // Directory path for saving sub resources and sub html frames. |
| 57 FilePath dir; |
| 58 |
| 59 SavePackageParam(const std::string& mime_type) |
| 60 : current_tab_mime_type(mime_type) { } |
| 61 }; |
| 62 |
| 47 namespace { | 63 namespace { |
| 48 | 64 |
| 49 // Default name which will be used when we can not get proper name from | 65 // Default name which will be used when we can not get proper name from |
| 50 // resource URL. | 66 // resource URL. |
| 51 const wchar_t kDefaultSaveName[] = L"saved_resource"; | 67 const wchar_t kDefaultSaveName[] = L"saved_resource"; |
| 52 | 68 |
| 53 // Maximum number of file ordinal number. I think it's big enough for resolving | 69 // Maximum number of file ordinal number. I think it's big enough for resolving |
| 54 // name-conflict files which has same base file name. | 70 // name-conflict files which has same base file name. |
| 55 const int32 kMaxFileOrdinalNumber = 9999; | 71 const int32 kMaxFileOrdinalNumber = 9999; |
| 56 | 72 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 86 | 102 |
| 87 for (FilePath::StringType::size_type i = l_paren_index + 1; | 103 for (FilePath::StringType::size_type i = l_paren_index + 1; |
| 88 i != r_paren_index; ++i) { | 104 i != r_paren_index; ++i) { |
| 89 if (!IsAsciiDigit(pure_file_name[i])) | 105 if (!IsAsciiDigit(pure_file_name[i])) |
| 90 return pure_file_name; | 106 return pure_file_name; |
| 91 } | 107 } |
| 92 | 108 |
| 93 return pure_file_name.substr(0, l_paren_index); | 109 return pure_file_name.substr(0, l_paren_index); |
| 94 } | 110 } |
| 95 | 111 |
| 96 // In testing mode, |should_prompt_user| will be false, and we simply set the | |
| 97 // final name as the suggested name. Otherwise we pop up a Save As dialog. | |
| 98 bool SaveFileAsWithFilter(gfx::NativeView owner, | |
| 99 const std::wstring& suggested_name, | |
| 100 const std::wstring& filter, | |
| 101 const std::wstring& def_ext, | |
| 102 bool ignore_suggested_ext, | |
| 103 unsigned* index, | |
| 104 std::wstring* final_name, | |
| 105 bool should_prompt_user) { | |
| 106 // TODO(port): Until we have an equivalent call on other platforms, assume | |
| 107 // |suggested_name| will work just fine. | |
| 108 #if defined(OS_WIN) | |
| 109 if (should_prompt_user) | |
| 110 return win_util::SaveFileAsWithFilter(owner, | |
| 111 suggested_name, | |
| 112 filter, | |
| 113 def_ext, | |
| 114 ignore_suggested_ext, | |
| 115 index, | |
| 116 final_name); | |
| 117 #elif defined(OS_POSIX) | |
| 118 NOTIMPLEMENTED(); | |
| 119 #endif | |
| 120 | |
| 121 final_name->assign(suggested_name); | |
| 122 return true; | |
| 123 } | |
| 124 | |
| 125 // As above, in testing mode, just assign |final_name| to be |suggested_name|. | |
| 126 bool SaveFileAs(gfx::NativeView owner, | |
| 127 const std::wstring& suggested_name, | |
| 128 std::wstring* final_name, | |
| 129 bool should_prompt_user) { | |
| 130 // TODO(port): Until we have an equivalent call on other platforms, assume | |
| 131 // |suggested_name| will work just fine. | |
| 132 #if defined(OS_WIN) | |
| 133 if (should_prompt_user) | |
| 134 return win_util::SaveFileAs(owner, suggested_name, final_name); | |
| 135 #elif defined(OS_POSIX) | |
| 136 NOTIMPLEMENTED(); | |
| 137 #endif | |
| 138 | |
| 139 final_name->assign(suggested_name); | |
| 140 return true; | |
| 141 } | |
| 142 | |
| 143 } // namespace | 112 } // namespace |
| 144 | 113 |
| 145 SavePackage::SavePackage(WebContents* web_content, | 114 SavePackage::SavePackage(WebContents* web_content, |
| 146 SavePackageType save_type, | 115 SavePackageType save_type, |
| 147 const FilePath& file_full_path, | 116 const FilePath& file_full_path, |
| 148 const FilePath& directory_full_path) | 117 const FilePath& directory_full_path) |
| 149 : web_contents_(web_content), | 118 : web_contents_(web_content), |
| 150 download_(NULL), | 119 download_(NULL), |
| 151 saved_main_file_path_(file_full_path), | 120 saved_main_file_path_(file_full_path), |
| 152 saved_main_directory_path_(directory_full_path), | 121 saved_main_directory_path_(directory_full_path), |
| 153 finished_(false), | 122 finished_(false), |
| 154 user_canceled_(false), | 123 user_canceled_(false), |
| 155 disk_error_occurred_(false), | 124 disk_error_occurred_(false), |
| 156 save_type_(save_type), | 125 save_type_(save_type), |
| 157 all_save_items_count_(0), | 126 all_save_items_count_(0), |
| 158 wait_state_(INITIALIZE), | 127 wait_state_(INITIALIZE), |
| 159 tab_id_(web_content->process()->pid()) { | 128 tab_id_(web_content->process()->pid()) { |
| 160 DCHECK(web_content); | 129 DCHECK(web_content); |
| 161 const GURL& current_page_url = web_contents_->GetURL(); | 130 const GURL& current_page_url = web_contents_->GetURL(); |
| 162 DCHECK(current_page_url.is_valid()); | 131 DCHECK(current_page_url.is_valid()); |
| 163 page_url_ = current_page_url; | 132 page_url_ = current_page_url; |
| 164 DCHECK(save_type_ == SAVE_AS_ONLY_HTML || | 133 DCHECK(save_type_ == SAVE_AS_ONLY_HTML || |
| 165 save_type_ == SAVE_AS_COMPLETE_HTML); | 134 save_type_ == SAVE_AS_COMPLETE_HTML); |
| 166 DCHECK(!saved_main_file_path_.empty() && | 135 DCHECK(!saved_main_file_path_.empty() && |
| 167 saved_main_file_path_.value().length() <= kMaxFilePathLength); | 136 saved_main_file_path_.value().length() <= kMaxFilePathLength); |
| 168 DCHECK(!saved_main_directory_path_.empty() && | 137 DCHECK(!saved_main_directory_path_.empty() && |
| 169 saved_main_directory_path_.value().length() < kMaxFilePathLength); | 138 saved_main_directory_path_.value().length() < kMaxFilePathLength); |
| 170 } | 139 } |
| 171 | 140 |
| 141 SavePackage::SavePackage(WebContents* web_contents) |
| 142 : web_contents_(web_contents), |
| 143 download_(NULL), |
| 144 finished_(false), |
| 145 user_canceled_(false), |
| 146 disk_error_occurred_(false), |
| 147 all_save_items_count_(0), |
| 148 wait_state_(INITIALIZE), |
| 149 tab_id_(web_contents->process()->pid()) { |
| 150 const GURL& current_page_url = web_contents_->GetURL(); |
| 151 DCHECK(current_page_url.is_valid()); |
| 152 page_url_ = current_page_url; |
| 153 } |
| 154 |
| 172 // This is for testing use. Set |finished_| as true because we don't want | 155 // This is for testing use. Set |finished_| as true because we don't want |
| 173 // method Cancel to be be called in destructor in test mode. | 156 // method Cancel to be be called in destructor in test mode. |
| 174 SavePackage::SavePackage(const FilePath& file_full_path, | 157 SavePackage::SavePackage(const FilePath& file_full_path, |
| 175 const FilePath& directory_full_path) | 158 const FilePath& directory_full_path) |
| 176 : download_(NULL), | 159 : download_(NULL), |
| 177 saved_main_file_path_(file_full_path), | 160 saved_main_file_path_(file_full_path), |
| 178 saved_main_directory_path_(directory_full_path), | 161 saved_main_directory_path_(directory_full_path), |
| 179 finished_(true), | 162 finished_(true), |
| 180 user_canceled_(false), | 163 user_canceled_(false), |
| 181 disk_error_occurred_(false), | 164 disk_error_occurred_(false), |
| (...skipping 29 matching lines...) Expand all Loading... |
| 211 | 194 |
| 212 if (download_) { | 195 if (download_) { |
| 213 // We call this to remove the view from the shelf. It will invoke | 196 // We call this to remove the view from the shelf. It will invoke |
| 214 // DownloadManager::RemoveDownload, but since the fake DownloadItem is not | 197 // DownloadManager::RemoveDownload, but since the fake DownloadItem is not |
| 215 // owned by DownloadManager, it will do nothing to our fake item. | 198 // owned by DownloadManager, it will do nothing to our fake item. |
| 216 download_->Remove(false); | 199 download_->Remove(false); |
| 217 delete download_; | 200 delete download_; |
| 218 download_ = NULL; | 201 download_ = NULL; |
| 219 } | 202 } |
| 220 file_manager_ = NULL; | 203 file_manager_ = NULL; |
| 204 |
| 205 // If there's an outstanding save dialog, make sure it doesn't call us back |
| 206 // now that we're gone. |
| 207 if (select_file_dialog_.get()) |
| 208 select_file_dialog_->ListenerDestroyed(); |
| 221 } | 209 } |
| 222 | 210 |
| 223 // Cancel all in progress request, might be called by user or internal error. | 211 // Cancel all in progress request, might be called by user or internal error. |
| 224 void SavePackage::Cancel(bool user_action) { | 212 void SavePackage::Cancel(bool user_action) { |
| 225 if (!canceled()) { | 213 if (!canceled()) { |
| 226 if (user_action) | 214 if (user_action) |
| 227 user_canceled_ = true; | 215 user_canceled_ = true; |
| 228 else | 216 else |
| 229 disk_error_occurred_ = true; | 217 disk_error_occurred_ = true; |
| 230 Stop(); | 218 Stop(); |
| (...skipping 758 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 989 // TODO(port): we need a version of ReplaceIllegalCharacters() that takes | 977 // TODO(port): we need a version of ReplaceIllegalCharacters() that takes |
| 990 // FilePaths. | 978 // FilePaths. |
| 991 file_util::ReplaceIllegalCharacters(&file_name, L' '); | 979 file_util::ReplaceIllegalCharacters(&file_name, L' '); |
| 992 TrimWhitespace(file_name, TRIM_ALL, &file_name); | 980 TrimWhitespace(file_name, TRIM_ALL, &file_name); |
| 993 FilePath suggest_name = FilePath::FromWStringHack(save_file_path.GetValue()); | 981 FilePath suggest_name = FilePath::FromWStringHack(save_file_path.GetValue()); |
| 994 suggest_name = suggest_name.Append(FilePath::FromWStringHack(file_name)); | 982 suggest_name = suggest_name.Append(FilePath::FromWStringHack(file_name)); |
| 995 | 983 |
| 996 return suggest_name; | 984 return suggest_name; |
| 997 } | 985 } |
| 998 | 986 |
| 999 // Static. | 987 void SavePackage::GetSaveInfo() { |
| 1000 bool SavePackage::GetSaveInfo(const FilePath& suggest_name, | 988 #if defined(OS_WIN) |
| 1001 gfx::NativeView container_window, | 989 // Use "Web Page, Complete" option as default choice of saving page. |
| 1002 SavePackageParam* param, | 990 int filter_index = 2; |
| 1003 DownloadManager* download_manager) { | 991 std::wstring filter; |
| 1004 // TODO(tc): It might be nice to move this code into the download | 992 std::wstring default_extension; |
| 1005 // manager. http://crbug.com/6025 | 993 FilePath title = |
| 994 FilePath::FromWStringHack(UTF16ToWideHack(web_contents_->GetTitle())); |
| 995 FilePath suggested_path = |
| 996 GetSuggestNameForSaveAs(web_contents_->profile()->GetPrefs(), title); |
| 997 std::wstring suggested_name = suggested_path.ToWStringHack(); |
| 1006 | 998 |
| 1007 // Use "Web Page, Complete" option as default choice of saving page. | 999 SavePackageParam* save_params = |
| 1008 unsigned index = 2; | 1000 new SavePackageParam(web_contents_->contents_mime_type()); |
| 1009 | 1001 |
| 1010 // If the contents can not be saved as complete-HTML, do not show the | 1002 // If the contents can not be saved as complete-HTML, do not show the |
| 1011 // file filters. | 1003 // file filters. |
| 1012 if (CanSaveAsComplete(param->current_tab_mime_type)) { | 1004 if (CanSaveAsComplete(save_params->current_tab_mime_type)) { |
| 1013 // Create filter string. | 1005 filter = l10n_util::GetString(IDS_SAVE_PAGE_FILTER); |
| 1014 std::wstring filter = l10n_util::GetString(IDS_SAVE_PAGE_FILTER); | |
| 1015 filter.resize(filter.size() + 2); | 1006 filter.resize(filter.size() + 2); |
| 1016 filter[filter.size() - 1] = L'\0'; | 1007 filter[filter.size() - 1] = L'\0'; |
| 1017 filter[filter.size() - 2] = L'\0'; | 1008 filter[filter.size() - 2] = L'\0'; |
| 1018 | 1009 default_extension = L"htm"; |
| 1019 // Since we take the suggested name from the web page's title, we want to | |
| 1020 // ignore the file extension generated by SaveFileAsWithFilter, since it | |
| 1021 // will always be ".htm". | |
| 1022 std::wstring main_file_path; | |
| 1023 bool success = SaveFileAsWithFilter(container_window, | |
| 1024 suggest_name.ToWStringHack(), filter, L"htm", true, &index, | |
| 1025 &main_file_path, g_should_prompt_for_filename); | |
| 1026 param->saved_main_file_path = FilePath::FromWStringHack(main_file_path); | |
| 1027 if (!success) | |
| 1028 return false; | |
| 1029 } else { | 1010 } else { |
| 1030 std::wstring main_file_path; | 1011 filter = win_util::GetFileFilterFromPath(suggested_name); |
| 1031 bool success = SaveFileAs(container_window, suggest_name.ToWStringHack(), | 1012 filter_index = 1; |
| 1032 &main_file_path, g_should_prompt_for_filename); | |
| 1033 param->saved_main_file_path = FilePath::FromWStringHack(main_file_path); | |
| 1034 if (!success) | |
| 1035 return false; | |
| 1036 | |
| 1037 // Set save-as type to only-HTML if the contents of current tab can not be | |
| 1038 // saved as complete-HTML. | |
| 1039 index = 1; | |
| 1040 } | 1013 } |
| 1041 | 1014 |
| 1015 if (g_should_prompt_for_filename) { |
| 1016 if (!select_file_dialog_.get()) |
| 1017 select_file_dialog_ = SelectFileDialog::Create(this); |
| 1018 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE, |
| 1019 std::wstring(), |
| 1020 suggested_name, |
| 1021 filter, |
| 1022 filter_index, |
| 1023 default_extension, |
| 1024 GetAncestor(web_contents_->GetNativeView(), |
| 1025 GA_ROOT), |
| 1026 save_params); |
| 1027 } else { |
| 1028 // Just use 'suggested_name' instead of opening the dialog prompt. |
| 1029 ContinueSave(save_params, suggested_name, filter_index); |
| 1030 delete save_params; |
| 1031 } |
| 1032 #else |
| 1033 NOTIMPLEMENTED(); |
| 1034 #endif // OS_WIN |
| 1035 } |
| 1036 |
| 1037 // Called after the save file dialog box returns. |
| 1038 void SavePackage::ContinueSave(SavePackageParam* param, |
| 1039 const std::wstring& final_name, |
| 1040 int index) { |
| 1042 // Ensure the filename is safe. | 1041 // Ensure the filename is safe. |
| 1043 download_manager->GenerateSafeFilename(param->current_tab_mime_type, | 1042 param->saved_main_file_path = FilePath::FromWStringHack(final_name); |
| 1044 ¶m->saved_main_file_path); | 1043 DownloadManager* dlm = web_contents_->profile()->GetDownloadManager(); |
| 1044 DCHECK(dlm); |
| 1045 dlm->GenerateSafeFilename(param->current_tab_mime_type, |
| 1046 ¶m->saved_main_file_path); |
| 1045 | 1047 |
| 1046 // The option index is not zero-based. | 1048 // The option index is not zero-based. |
| 1047 DCHECK(index > 0 && index < 3); | 1049 DCHECK(index > 0 && index < 3); |
| 1048 param->dir = param->saved_main_file_path.DirName(); | 1050 param->dir = param->saved_main_file_path.DirName(); |
| 1049 | 1051 |
| 1052 PrefService* prefs = web_contents_->profile()->GetPrefs(); |
| 1050 StringPrefMember save_file_path; | 1053 StringPrefMember save_file_path; |
| 1051 save_file_path.Init(prefs::kSaveFileDefaultDirectory, param->prefs, NULL); | 1054 save_file_path.Init(prefs::kSaveFileDefaultDirectory, prefs, NULL); |
| 1052 // If user change the default saving directory, we will remember it just | 1055 // If user change the default saving directory, we will remember it just |
| 1053 // like IE and FireFox. | 1056 // like IE and FireFox. |
| 1054 if (save_file_path.GetValue() != param->dir.ToWStringHack()) | 1057 if (save_file_path.GetValue() != param->dir.ToWStringHack()) |
| 1055 save_file_path.SetValue(param->dir.ToWStringHack()); | 1058 save_file_path.SetValue(param->dir.ToWStringHack()); |
| 1056 | 1059 |
| 1057 param->save_type = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML : | 1060 param->save_type = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML : |
| 1058 SavePackage::SAVE_AS_COMPLETE_HTML; | 1061 SavePackage::SAVE_AS_COMPLETE_HTML; |
| 1059 | 1062 |
| 1060 if (param->save_type == SavePackage::SAVE_AS_COMPLETE_HTML) { | 1063 if (param->save_type == SavePackage::SAVE_AS_COMPLETE_HTML) { |
| 1061 // Make new directory for saving complete file. | 1064 // Make new directory for saving complete file. |
| 1062 param->dir = param->dir.Append( | 1065 param->dir = param->dir.Append( |
| 1063 param->saved_main_file_path.RemoveExtension().BaseName().value() + | 1066 param->saved_main_file_path.RemoveExtension().BaseName().value() + |
| 1064 FILE_PATH_LITERAL("_files")); | 1067 FILE_PATH_LITERAL("_files")); |
| 1065 } | 1068 } |
| 1066 | 1069 |
| 1067 return true; | 1070 save_type_ = param->save_type; |
| 1071 saved_main_file_path_ = param->saved_main_file_path; |
| 1072 saved_main_directory_path_ = param->dir; |
| 1073 |
| 1074 Init(); |
| 1068 } | 1075 } |
| 1069 | 1076 |
| 1070 // Static | 1077 // Static |
| 1071 bool SavePackage::IsSavableURL(const GURL& url) { | 1078 bool SavePackage::IsSavableURL(const GURL& url) { |
| 1072 return url.SchemeIs(chrome::kHttpScheme) || | 1079 return url.SchemeIs(chrome::kHttpScheme) || |
| 1073 url.SchemeIs(chrome::kHttpsScheme) || | 1080 url.SchemeIs(chrome::kHttpsScheme) || |
| 1074 url.SchemeIs(chrome::kFileScheme) || | 1081 url.SchemeIs(chrome::kFileScheme) || |
| 1075 url.SchemeIs(chrome::kFtpScheme); | 1082 url.SchemeIs(chrome::kFtpScheme); |
| 1076 } | 1083 } |
| 1077 | 1084 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1112 if (available_length > 0) { | 1119 if (available_length > 0) { |
| 1113 *pure_file_name = | 1120 *pure_file_name = |
| 1114 pure_file_name->substr(0, available_length); | 1121 pure_file_name->substr(0, available_length); |
| 1115 return true; | 1122 return true; |
| 1116 } | 1123 } |
| 1117 | 1124 |
| 1118 // Not enough room to even use a shortened |pure_file_name|. | 1125 // Not enough room to even use a shortened |pure_file_name|. |
| 1119 pure_file_name->clear(); | 1126 pure_file_name->clear(); |
| 1120 return false; | 1127 return false; |
| 1121 } | 1128 } |
| 1129 |
| 1130 // SelectFileDialog::Listener interface. |
| 1131 void SavePackage::FileSelected(const std::wstring& path, |
| 1132 int index, void* params) { |
| 1133 SavePackageParam* save_params = reinterpret_cast<SavePackageParam*>(params); |
| 1134 ContinueSave(save_params, path, index); |
| 1135 delete save_params; |
| 1136 } |
| 1137 |
| 1138 void SavePackage::FileSelectionCanceled(void* params) { |
| 1139 SavePackageParam* save_params = reinterpret_cast<SavePackageParam*>(params); |
| 1140 delete save_params; |
| 1141 } |
| OLD | NEW |