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/download_shelf_context_menu.h" 6 7 #include "base/command_line.h" 8 #include "chrome/browser/browser_process.h" 9 #include "chrome/browser/download/download_crx_util.h" 10 #include "chrome/browser/download/download_item_model.h" 11 #include "chrome/browser/download/download_prefs.h" 12 #include "chrome/browser/safe_browsing/download_protection_service.h" 13 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 14 #include "chrome/common/extensions/extension.h" 15 #include "chrome/common/url_constants.h" 16 #include "content/public/browser/download_item.h" 17 #include "content/public/browser/download_manager.h" 18 #include "content/public/browser/page_navigator.h" 19 #include "content/public/common/content_switches.h" 20 #include "grit/generated_resources.h" 21 #include "ui/base/l10n/l10n_util.h" 22 23 using content::DownloadItem; 24 using extensions::Extension; 25 26 namespace { 27 28 // Returns true if downloads resumption is enabled. 29 bool IsDownloadResumptionEnabled() { 30 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 31 return command_line.HasSwitch(switches::kEnableDownloadResumption); 32 } 33 34 } // namespace 35 36 DownloadShelfContextMenu::~DownloadShelfContextMenu() { 37 DetachFromDownloadItem(); 38 } 39 40 DownloadShelfContextMenu::DownloadShelfContextMenu( 41 DownloadItem* download_item, 42 content::PageNavigator* navigator) 43 : download_item_(download_item), 44 navigator_(navigator) { 45 DCHECK(download_item_); 46 download_item_->AddObserver(this); 47 } 48 49 ui::SimpleMenuModel* DownloadShelfContextMenu::GetMenuModel() { 50 ui::SimpleMenuModel* model = NULL; 51 52 if (!download_item_) 53 return NULL; 54 55 DownloadItemModel download_model(download_item_); 56 // We shouldn't be opening a context menu for a dangerous download, unless it 57 // is a malicious download. 58 DCHECK(!download_model.IsDangerous() || download_model.IsMalicious()); 59 60 if (download_model.IsMalicious()) 61 model = GetMaliciousMenuModel(); 62 else if (download_item_->GetState() == DownloadItem::COMPLETE) 63 model = GetFinishedMenuModel(); 64 else if (download_item_->GetState() == DownloadItem::INTERRUPTED) 65 model = GetInterruptedMenuModel(); 66 else 67 model = GetInProgressMenuModel(); 68 return model; 69 } 70 71 bool DownloadShelfContextMenu::IsCommandIdEnabled(int command_id) const { 72 if (!download_item_) 73 return false; 74 75 switch (static_cast<ContextMenuCommands>(command_id)) { 76 case SHOW_IN_FOLDER: 77 return download_item_->CanShowInFolder(); 78 case OPEN_WHEN_COMPLETE: 79 return download_item_->CanOpenDownload() && 80 !download_crx_util::IsExtensionDownload(*download_item_); 81 case ALWAYS_OPEN_TYPE: 82 // For temporary downloads, the target filename might be a temporary 83 // filename. Don't base an "Always open" decision based on it. Also 84 // exclude extensions. 85 return download_item_->CanOpenDownload() && 86 !download_crx_util::IsExtensionDownload(*download_item_); 87 case CANCEL: 88 return !download_item_->IsDone(); 89 case TOGGLE_PAUSE: 90 return !download_item_->IsDone(); 91 case DISCARD: 92 case KEEP: 93 case LEARN_MORE_SCANNING: 94 case LEARN_MORE_INTERRUPTED: 95 return true; 96 } 97 return false; 98 } 99 100 bool DownloadShelfContextMenu::IsCommandIdChecked(int command_id) const { 101 if (!download_item_) 102 return false; 103 104 switch (command_id) { 105 case OPEN_WHEN_COMPLETE: 106 return download_item_->GetOpenWhenComplete() || 107 download_crx_util::IsExtensionDownload(*download_item_); 108 case ALWAYS_OPEN_TYPE: 109 return download_item_->ShouldOpenFileBasedOnExtension(); 110 case TOGGLE_PAUSE: 111 return download_item_->IsPaused(); 112 } 113 return false; 114 } 115 116 void DownloadShelfContextMenu::ExecuteCommand(int command_id, int event_flags) { 117 if (!download_item_) 118 return; 119 120 switch (static_cast<ContextMenuCommands>(command_id)) { 121 case SHOW_IN_FOLDER: 122 download_item_->ShowDownloadInShell(); 123 break; 124 case OPEN_WHEN_COMPLETE: 125 download_item_->OpenDownload(); 126 break; 127 case ALWAYS_OPEN_TYPE: { 128 DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext( 129 download_item_->GetBrowserContext()); 130 base::FilePath path = download_item_->GetTargetFilePath(); 131 if (!IsCommandIdChecked(ALWAYS_OPEN_TYPE)) 132 prefs->EnableAutoOpenBasedOnExtension(path); 133 else 134 prefs->DisableAutoOpenBasedOnExtension(path); 135 break; 136 } 137 case CANCEL: 138 download_item_->Cancel(true /* Cancelled by user */); 139 break; 140 case TOGGLE_PAUSE: 141 if (download_item_->GetState() == DownloadItem::IN_PROGRESS && 142 !download_item_->IsPaused()) { 143 download_item_->Pause(); 144 } else { 145 download_item_->Resume(); 146 } 147 break; 148 case DISCARD: 149 download_item_->Remove(); 150 break; 151 case KEEP: 152 download_item_->ValidateDangerousDownload(); 153 break; 154 case LEARN_MORE_SCANNING: { 155 #if defined(FULL_SAFE_BROWSING) 156 using safe_browsing::DownloadProtectionService; 157 SafeBrowsingService* sb_service = 158 g_browser_process->safe_browsing_service(); 159 DownloadProtectionService* protection_service = 160 (sb_service ? sb_service->download_protection_service() : NULL); 161 if (protection_service) { 162 protection_service->ShowDetailsForDownload(*download_item_, navigator_); 163 } 164 #else 165 // Should only be getting invoked if we are using safe browsing. 166 NOTREACHED(); 167 #endif 168 break; 169 } 170 case LEARN_MORE_INTERRUPTED: 171 navigator_->OpenURL( 172 content::OpenURLParams(GURL(chrome::kDownloadInterruptedLearnMoreURL), 173 content::Referrer(), 174 NEW_FOREGROUND_TAB, 175 content::PAGE_TRANSITION_LINK, 176 false)); 177 break; 178 } 179 } 180 181 bool DownloadShelfContextMenu::GetAcceleratorForCommandId( 182 int command_id, ui::Accelerator* accelerator) { 183 return false; 184 } 185 186 bool DownloadShelfContextMenu::IsItemForCommandIdDynamic(int command_id) const { 187 return command_id == TOGGLE_PAUSE; 188 } 189 190 string16 DownloadShelfContextMenu::GetLabelForCommandId(int command_id) const { 191 switch (static_cast<ContextMenuCommands>(command_id)) { 192 case SHOW_IN_FOLDER: 193 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_SHOW); 194 case OPEN_WHEN_COMPLETE: 195 if (download_item_ && !download_item_->IsDone()) 196 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); 197 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN); 198 case ALWAYS_OPEN_TYPE: 199 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 200 case CANCEL: 201 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_CANCEL); 202 case TOGGLE_PAUSE: 203 if (download_item_ && 204 download_item_->GetState() == DownloadItem::IN_PROGRESS && 205 !download_item_->IsPaused()) 206 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_PAUSE_ITEM); 207 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_RESUME_ITEM); 208 case DISCARD: 209 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_DISCARD); 210 case KEEP: 211 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_KEEP); 212 case LEARN_MORE_SCANNING: 213 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); 214 case LEARN_MORE_INTERRUPTED: 215 return l10n_util::GetStringUTF16( 216 IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); 217 } 218 NOTREACHED(); 219 return string16(); 220 } 221 222 void DownloadShelfContextMenu::DetachFromDownloadItem() { 223 if (!download_item_) 224 return; 225 226 download_item_->RemoveObserver(this); 227 download_item_ = NULL; 228 } 229 230 void DownloadShelfContextMenu::OnDownloadDestroyed(DownloadItem* download) { 231 DCHECK(download_item_ == download); 232 DetachFromDownloadItem(); 233 } 234 235 ui::SimpleMenuModel* DownloadShelfContextMenu::GetInProgressMenuModel() { 236 if (in_progress_download_menu_model_) 237 return in_progress_download_menu_model_.get(); 238 239 in_progress_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 240 241 in_progress_download_menu_model_->AddCheckItemWithStringId( 242 OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); 243 in_progress_download_menu_model_->AddCheckItemWithStringId( 244 ALWAYS_OPEN_TYPE, IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 245 in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 246 in_progress_download_menu_model_->AddItemWithStringId( 247 TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_PAUSE_ITEM); 248 in_progress_download_menu_model_->AddItemWithStringId( 249 SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); 250 in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 251 in_progress_download_menu_model_->AddItemWithStringId( 252 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 253 254 return in_progress_download_menu_model_.get(); 255 } 256 257 ui::SimpleMenuModel* DownloadShelfContextMenu::GetFinishedMenuModel() { 258 if (finished_download_menu_model_) 259 return finished_download_menu_model_.get(); 260 261 finished_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 262 263 finished_download_menu_model_->AddItemWithStringId( 264 OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN); 265 finished_download_menu_model_->AddCheckItemWithStringId( 266 ALWAYS_OPEN_TYPE, IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 267 finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 268 finished_download_menu_model_->AddItemWithStringId( 269 SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); 270 finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 271 finished_download_menu_model_->AddItemWithStringId( 272 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 273 274 return finished_download_menu_model_.get(); 275 } 276 277 ui::SimpleMenuModel* DownloadShelfContextMenu::GetInterruptedMenuModel() { 278 #if !defined(OS_WIN) 279 // If resumption isn't enabled and we aren't on Windows, then none of the 280 // options here are applicable. 281 if (!IsDownloadResumptionEnabled()) 282 return GetInProgressMenuModel(); 283 #endif 284 285 if (interrupted_download_menu_model_) 286 return interrupted_download_menu_model_.get(); 287 288 interrupted_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 289 290 if (IsDownloadResumptionEnabled()) { 291 interrupted_download_menu_model_->AddItemWithStringId( 292 TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_RESUME_ITEM); 293 } 294 #if defined(OS_WIN) 295 // The Help Center article is currently Windows specific. 296 // TODO(asanka): Enable this for other platforms when the article is expanded 297 // for other platforms. 298 interrupted_download_menu_model_->AddItemWithStringId( 299 LEARN_MORE_INTERRUPTED, IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); 300 #endif 301 if (IsDownloadResumptionEnabled()) { 302 interrupted_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 303 interrupted_download_menu_model_->AddItemWithStringId( 304 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 305 } 306 307 return interrupted_download_menu_model_.get(); 308 } 309 310 ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaliciousMenuModel() { 311 if (malicious_download_menu_model_) 312 return malicious_download_menu_model_.get(); 313 314 malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 315 316 malicious_download_menu_model_->AddItemWithStringId( 317 DISCARD, IDS_DOWNLOAD_MENU_DISCARD); 318 malicious_download_menu_model_->AddItemWithStringId( 319 KEEP, IDS_DOWNLOAD_MENU_KEEP); 320 malicious_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 321 malicious_download_menu_model_->AddItemWithStringId( 322 LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); 323 324 return malicious_download_menu_model_.get(); 325 } 326