OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 #import "chrome/browser/cocoa/extensions/extension_action_context_menu.h" |
| 6 |
| 7 #include "app/l10n_util_mac.h" |
| 8 #include "base/sys_string_conversions.h" |
| 9 #include "base/task.h" |
| 10 #include "chrome/browser/browser_list.h" |
| 11 #include "chrome/browser/extensions/extension_install_ui.h" |
| 12 #include "chrome/browser/extensions/extensions_service.h" |
| 13 #include "chrome/browser/profile.h" |
| 14 #include "chrome/common/extensions/extension.h" |
| 15 #include "chrome/common/extensions/extension_constants.h" |
| 16 #include "chrome/common/url_constants.h" |
| 17 #include "grit/generated_resources.h" |
| 18 |
| 19 // A class that loads the extension icon on the I/O thread before showing the |
| 20 // confirmation dialog to uninstall the given extension. |
| 21 // Also acts as the extension's UI delegate in order to display the dialog. |
| 22 class AsyncUninstaller : public base::RefCountedThreadSafe<AsyncUninstaller>, |
| 23 public ExtensionInstallUI::Delegate { |
| 24 public: |
| 25 AsyncUninstaller(Extension* extension) : extension_(extension) {} |
| 26 |
| 27 // Load the uninstall icon then show the confirmation dialog when finished. |
| 28 void LoadExtensionIconThenConfirmUninstall() { |
| 29 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, |
| 30 NewRunnableMethod(this, &AsyncUninstaller::LoadIconOnFileThread, |
| 31 &uninstall_icon_)); |
| 32 } |
| 33 |
| 34 void Cancel() { |
| 35 uninstall_icon_.reset(); |
| 36 extension_ = NULL; |
| 37 } |
| 38 |
| 39 // Overridden by ExtensionInstallUI::Delegate. |
| 40 virtual void InstallUIProceed() { |
| 41 Browser* browser = BrowserList::GetLastActive(); |
| 42 // GetLastActive() returns NULL during testing. |
| 43 if (!browser) |
| 44 return; |
| 45 browser->profile()->GetExtensionsService()-> |
| 46 UninstallExtension(extension_->id(), false); |
| 47 } |
| 48 |
| 49 virtual void InstallUIAbort() {} |
| 50 |
| 51 private: |
| 52 friend class base::RefCountedThreadSafe<AsyncUninstaller>; |
| 53 ~AsyncUninstaller() {} |
| 54 |
| 55 void LoadIconOnFileThread(scoped_ptr<SkBitmap>* uninstall_icon) { |
| 56 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 57 Extension::DecodeIcon(extension_, Extension::EXTENSION_ICON_LARGE, |
| 58 uninstall_icon); |
| 59 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, |
| 60 NewRunnableMethod(this, &AsyncUninstaller::ShowConfirmationDialog, |
| 61 uninstall_icon)); |
| 62 } |
| 63 |
| 64 void ShowConfirmationDialog(scoped_ptr<SkBitmap>* uninstall_icon) { |
| 65 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 66 // If |extension_| is NULL, then the action was cancelled. Bail. |
| 67 if (!extension_) |
| 68 return; |
| 69 |
| 70 Browser* browser = BrowserList::GetLastActive(); |
| 71 // GetLastActive() returns NULL during testing. |
| 72 if (!browser) |
| 73 return; |
| 74 |
| 75 ExtensionInstallUI client(browser->profile()); |
| 76 client.ConfirmUninstall(this, extension_, uninstall_icon->get()); |
| 77 } |
| 78 |
| 79 // The extension that we're loading the icon for. Weak. |
| 80 Extension* extension_; |
| 81 |
| 82 // The uninstall icon shown by the confirmation dialog. |
| 83 scoped_ptr<SkBitmap> uninstall_icon_; |
| 84 |
| 85 DISALLOW_COPY_AND_ASSIGN(AsyncUninstaller); |
| 86 }; |
| 87 |
| 88 @interface ExtensionActionContextMenu(Private) |
| 89 // Callback for the context menu items. |
| 90 - (void)dispatch:(id)menuItem; |
| 91 @end |
| 92 |
| 93 @implementation ExtensionActionContextMenu |
| 94 |
| 95 namespace { |
| 96 // Enum of menu item choices to their respective indices. |
| 97 // NOTE: You MUST keep this in sync with the |menuItems| NSArray below. |
| 98 enum { |
| 99 kExtensionContextName = 0, |
| 100 kExtensionContextOptions = 2, |
| 101 kExtensionContextDisable = 3, |
| 102 kExtensionContextUninstall = 4, |
| 103 kExtensionContextManage = 6 |
| 104 }; |
| 105 } // namespace |
| 106 |
| 107 - (id)initWithExtension:(Extension*)extension { |
| 108 if ((self = [super initWithTitle:@""])) { |
| 109 extension_ = extension; |
| 110 |
| 111 NSArray* menuItems = [NSArray arrayWithObjects: |
| 112 base::SysUTF8ToNSString(extension->name()), |
| 113 [NSMenuItem separatorItem], |
| 114 l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_OPTIONS), |
| 115 l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_DISABLE), |
| 116 l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_UNINSTALL), |
| 117 [NSMenuItem separatorItem], |
| 118 l10n_util::GetNSStringWithFixup(IDS_MANAGE_EXTENSIONS), |
| 119 nil]; |
| 120 |
| 121 for (id item in menuItems) { |
| 122 if ([item isKindOfClass:[NSMenuItem class]]) { |
| 123 [self addItem:item]; |
| 124 } else if ([item isKindOfClass:[NSString class]]) { |
| 125 NSMenuItem* itemObj = [self addItemWithTitle:item |
| 126 action:@selector(dispatch:) |
| 127 keyEquivalent:@""]; |
| 128 // The tag should correspond to the enum above. |
| 129 // NOTE: The enum and the order of the menu items MUST be in sync. |
| 130 [itemObj setTag:[self indexOfItem:itemObj]]; |
| 131 |
| 132 // Disable the 'Options' item if there are no options to set. |
| 133 if ([itemObj tag] == kExtensionContextOptions && |
| 134 extension_->options_url().spec().length() <= 0) { |
| 135 // Setting the target to nil will disable the item. For some reason |
| 136 // setDisabled:NO does not work. |
| 137 [itemObj setTarget:nil]; |
| 138 } else { |
| 139 [itemObj setTarget:self]; |
| 140 } |
| 141 } |
| 142 } |
| 143 |
| 144 return self; |
| 145 } |
| 146 return nil; |
| 147 } |
| 148 |
| 149 - (void)dispatch:(id)menuItem { |
| 150 Browser* browser = BrowserList::GetLastActive(); |
| 151 // GetLastActive() returns NULL during testing. |
| 152 if (!browser) |
| 153 return; |
| 154 |
| 155 Profile* profile = browser->profile(); |
| 156 |
| 157 NSMenuItem* item = (NSMenuItem*)menuItem; |
| 158 switch ([item tag]) { |
| 159 case kExtensionContextName: { |
| 160 GURL url(std::string(extension_urls::kGalleryBrowsePrefix) + |
| 161 std::string("/detail/") + extension_->id()); |
| 162 browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); |
| 163 break; |
| 164 } |
| 165 case kExtensionContextOptions: { |
| 166 DCHECK(!extension_->options_url().is_empty()); |
| 167 browser->OpenURL(extension_->options_url(), GURL(), |
| 168 NEW_FOREGROUND_TAB, PageTransition::LINK); |
| 169 break; |
| 170 } |
| 171 case kExtensionContextDisable: { |
| 172 ExtensionsService* extension_service = profile->GetExtensionsService(); |
| 173 extension_service->DisableExtension(extension_->id()); |
| 174 break; |
| 175 } |
| 176 case kExtensionContextUninstall: { |
| 177 if (uninstaller_.get()) |
| 178 uninstaller_->Cancel(); |
| 179 |
| 180 uninstaller_ = new AsyncUninstaller(extension_); |
| 181 uninstaller_->LoadExtensionIconThenConfirmUninstall(); |
| 182 break; |
| 183 } |
| 184 case kExtensionContextManage: { |
| 185 browser->OpenURL(GURL(chrome::kChromeUIExtensionsURL), GURL(), |
| 186 NEW_FOREGROUND_TAB, PageTransition::LINK); |
| 187 break; |
| 188 } |
| 189 default: |
| 190 NOTREACHED(); |
| 191 break; |
| 192 } |
| 193 } |
| 194 |
| 195 @end |
OLD | NEW |