summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field.mm27
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.h6
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.mm4
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_editor.mm48
-rw-r--r--chrome/browser/cocoa/extensions/browser_actions_controller.mm44
-rw-r--r--chrome/browser/cocoa/extensions/extension_action_context_menu.h33
-rw-r--r--chrome/browser/cocoa/extensions/extension_action_context_menu.mm195
7 files changed, 320 insertions, 37 deletions
diff --git a/chrome/browser/cocoa/autocomplete_text_field.mm b/chrome/browser/cocoa/autocomplete_text_field.mm
index e7fc037..0697a92 100644
--- a/chrome/browser/cocoa/autocomplete_text_field.mm
+++ b/chrome/browser/cocoa/autocomplete_text_field.mm
@@ -70,15 +70,16 @@
// a decoration area and get the expected selection behaviour,
// likewise for multiple clicks in those areas.
- (void)mouseDown:(NSEvent*)theEvent {
- const NSPoint locationInWindow = [theEvent locationInWindow];
- const NSPoint location = [self convertPoint:locationInWindow fromView:nil];
+ const NSPoint location =
+ [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ const NSRect bounds([self bounds]);
AutocompleteTextFieldCell* cell = [self autocompleteTextFieldCell];
- const NSRect textFrame([cell textFrameForFrame:[self bounds]]);
+ const NSRect textFrame([cell textFrameForFrame:bounds]);
// A version of the textFrame which extends across the field's
// entire width.
- const NSRect bounds([self bounds]);
+
const NSRect fullFrame(NSMakeRect(bounds.origin.x, textFrame.origin.y,
bounds.size.width, textFrame.size.height));
@@ -88,8 +89,9 @@
// above/below test is needed because NSTextView treats mouse events
// above/below as select-to-end-in-that-direction, which makes
// things janky.
- if (NSMouseInRect(location, textFrame, [self isFlipped]) ||
- !NSMouseInRect(location, fullFrame, [self isFlipped])) {
+ BOOL flipped = [self isFlipped];
+ if (NSMouseInRect(location, textFrame, flipped) ||
+ !NSMouseInRect(location, fullFrame, flipped)) {
[super mouseDown:theEvent];
// After the event has been handled, if the current event is a
@@ -114,19 +116,18 @@
// If the user clicked the security hint icon in the cell, display the page
// info window.
- const NSRect hintIconFrame = [cell securityImageFrameForFrame:[self bounds]];
- if (NSMouseInRect(location, hintIconFrame, [self isFlipped])) {
+ const NSRect hintIconFrame = [cell securityImageFrameForFrame:bounds];
+ if (NSMouseInRect(location, hintIconFrame, flipped)) {
[cell onSecurityIconMousePressed];
return;
}
- // If the user clicked a Page Action icon, execute its action.
- const NSRect iconFrame([self bounds]);
+ const BOOL ctrlKey = ([theEvent modifierFlags] & NSControlKeyMask) != 0;
+ // If the user left-clicked a Page Action icon, execute its action.
const size_t pageActionCount = [cell pageActionCount];
for (size_t i = 0; i < pageActionCount; ++i) {
- NSRect pageActionFrame = [cell pageActionFrameForIndex:i inFrame:iconFrame];
- if (NSMouseInRect(location, pageActionFrame, [self isFlipped])) {
- // TODO(pamg): Do we need to send the event?
+ NSRect pageActionFrame = [cell pageActionFrameForIndex:i inFrame:bounds];
+ if (NSMouseInRect(location, pageActionFrame, flipped) && !ctrlKey) {
[cell onPageActionMousePressedIn:pageActionFrame forIndex:i];
return;
}
diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.h b/chrome/browser/cocoa/autocomplete_text_field_cell.h
index 13b0e7c..bcd595e 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_cell.h
+++ b/chrome/browser/cocoa/autocomplete_text_field_cell.h
@@ -9,6 +9,8 @@
#include "base/scoped_nsobject.h"
#include "chrome/browser/cocoa/location_bar_view_mac.h"
+class ExtensionAction;
+
// AutocompleteTextFieldCell extends StyledTextFieldCell to provide support for
// certain decorations to be applied to the field. These are the search hint
// ("Type to search" on the right-hand side), the keyword hint ("Press [Tab] to
@@ -76,6 +78,10 @@
// given index.
- (NSString*)pageActionToolTipForIndex:(size_t)index;
+// Returns a pointer to the ExtensionAction object that the view at the
+// specified index represents.
+- (ExtensionAction*)pageActionForIndex:(size_t)index;
+
// Called when the Page Action at the given index, whose icon is drawn in the
// iconFrame, is visible and clicked. Passed through to the list of views to
// handle the click.
diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.mm b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
index 9df998b..8f54193 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_cell.mm
+++ b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
@@ -342,6 +342,10 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) {
return page_action_views_->ViewAt(index)->GetToolTip();
}
+- (ExtensionAction*)pageActionForIndex:(size_t)index {
+ return page_action_views_->ViewAt(index)->page_action();
+}
+
- (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
DCHECK(hintString_);
diff --git a/chrome/browser/cocoa/autocomplete_text_field_editor.mm b/chrome/browser/cocoa/autocomplete_text_field_editor.mm
index 54a866e..cf6e77b 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_editor.mm
+++ b/chrome/browser/cocoa/autocomplete_text_field_editor.mm
@@ -9,9 +9,21 @@
#include "grit/generated_resources.h"
#include "base/sys_string_conversions.h"
#include "chrome/app/chrome_dll_resource.h" // IDC_*
+#include "chrome/browser/browser_list.h"
#import "chrome/browser/cocoa/autocomplete_text_field.h"
+#import "chrome/browser/cocoa/autocomplete_text_field_cell.h"
#import "chrome/browser/cocoa/browser_window_controller.h"
+#import "chrome/browser/cocoa/extensions/extension_action_context_menu.h"
#import "chrome/browser/cocoa/toolbar_controller.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/common/extensions/extension_action.h"
+
+class Extension;
+
+@interface AutocompleteTextFieldEditor(Private)
+// Returns the default context menu to be displayed on a right mouse click.
+- (NSMenu*)defaultMenuForEvent:(NSEvent*)event;
+@end
@implementation AutocompleteTextFieldEditor
@@ -78,14 +90,42 @@
// NSTextField and NSTextView synchronize their contents. That is
// probably unavoidable because in most cases having rich text in the
// field you probably would expect it to update the font panel.
-- (void)updateFontPanel {
-}
+- (void)updateFontPanel {}
// No ruler bar, so don't update any of that state, either.
-- (void)updateRuler {
-}
+- (void)updateRuler {}
- (NSMenu*)menuForEvent:(NSEvent*)event {
+ NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
+
+ // Was the right click within a Page Action? Show a different menu if so.
+ NSRect bounds([[self delegate] bounds]);
+ AutocompleteTextFieldCell* cell = [[self delegate] autocompleteTextFieldCell];
+ const size_t pageActionCount = [cell pageActionCount];
+ BOOL flipped = [self isFlipped];
+ Browser* browser = BrowserList::GetLastActive();
+ // GetLastActive() returns NULL during testing.
+ if (!browser)
+ return [self defaultMenuForEvent:event];
+ ExtensionsService* service = browser->profile()->GetExtensionsService();
+ for (size_t i = 0; i < pageActionCount; ++i) {
+ NSRect pageActionFrame = [cell pageActionFrameForIndex:i inFrame:bounds];
+ if (NSMouseInRect(location, pageActionFrame, flipped)) {
+ Extension* extension = service->GetExtensionById(
+ [cell pageActionForIndex:i]->extension_id(), false);
+ DCHECK(extension);
+ if (!extension)
+ break;
+ return [[[ExtensionActionContextMenu alloc] initWithExtension:extension]
+ autorelease];
+ }
+ }
+
+ // Otherwise, simply return the default menu for this instance.
+ return [self defaultMenuForEvent:event];
+}
+
+- (NSMenu*)defaultMenuForEvent:(NSEvent*)event {
NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"TITLE"] autorelease];
[menu addItemWithTitle:l10n_util::GetNSStringWithFixup(IDS_CUT)
action:@selector(cut:)
diff --git a/chrome/browser/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/cocoa/extensions/browser_actions_controller.mm
index f60dd88..345ba69 100644
--- a/chrome/browser/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/cocoa/extensions/browser_actions_controller.mm
@@ -9,6 +9,7 @@
#include "app/gfx/canvas_paint.h"
#include "base/sys_string_conversions.h"
#include "chrome/browser/browser.h"
+#include "chrome/browser/cocoa/extensions/extension_action_context_menu.h"
#include "chrome/browser/cocoa/extensions/extension_popup_controller.h"
#include "chrome/browser/cocoa/toolbar_button_cell.h"
#include "chrome/browser/extensions/extension_browser_event_router.h"
@@ -21,7 +22,7 @@
#include "chrome/common/notification_registrar.h"
#include "skia/ext/skia_utils_mac.h"
-static const CGFloat kBrowserActionBadgeOriginYOffset = -4;
+static const CGFloat kBrowserActionBadgeOriginYOffset = 5;
// Since the container is the maximum height of the toolbar, we have to move the
// buttons up by this amount in order to have them look vertically centered
@@ -37,12 +38,12 @@ extern const CGFloat kBrowserActionButtonPadding = 3;
NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged";
-@interface BrowserActionBadgeView : NSView {
+@interface BrowserActionCell : ToolbarButtonCell {
@private
- // The current tab ID used when drawing the badge.
+ // The current tab ID used when drawing the cell.
int tabId_;
- // The action we're drawing the badge for. Weak.
+ // The action we're drawing the cell for. Weak.
ExtensionAction* extensionAction_;
}
@@ -51,17 +52,18 @@ NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged";
@end
-@implementation BrowserActionBadgeView
+@implementation BrowserActionCell
+
+- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ [super drawWithFrame:cellFrame inView:controlView];
-- (void)drawRect:(NSRect)dirtyRect {
// CanvasPaint draws its content to the current NSGraphicsContext in its
// destructor. If anything needs to be drawn afterwards, then enclose this
// in a nested block.
- NSRect badgeBounds = [self bounds];
- badgeBounds.origin.y += kBrowserActionBadgeOriginYOffset;
- gfx::CanvasPaint canvas(badgeBounds, false);
+ cellFrame.origin.y += kBrowserActionBadgeOriginYOffset;
+ gfx::CanvasPaint canvas(cellFrame, false);
canvas.set_composite_alpha(true);
- gfx::Rect boundingRect(NSRectToCGRect(badgeBounds));
+ gfx::Rect boundingRect(NSRectToCGRect(cellFrame));
extensionAction_->PaintBadge(&canvas, boundingRect, tabId_);
}
@@ -80,8 +82,6 @@ class ExtensionImageTrackerBridge;
scoped_nsobject<NSImage> tabSpecificIcon_;
- scoped_nsobject<NSView> badgeView_;
-
// The extension for this button. Weak.
Extension* extension_;
@@ -162,6 +162,10 @@ class ExtensionImageTrackerBridge : public NotificationObserver,
@implementation BrowserActionButton
++ (Class)cellClass {
+ return [BrowserActionCell class];
+}
+
- (id)initWithExtension:(Extension*)extension
controller:(BrowserActionsController*)controller
xOffset:(int)xOffset {
@@ -170,13 +174,16 @@ class ExtensionImageTrackerBridge : public NotificationObserver,
kBrowserActionWidth,
kBrowserActionHeight);
if ((self = [super initWithFrame:frame])) {
- ToolbarButtonCell* cell = [[[ToolbarButtonCell alloc] init] autorelease];
+ BrowserActionCell* cell = [[[BrowserActionCell alloc] init] autorelease];
// [NSButton setCell:] warns to NOT use setCell: other than in the
// initializer of a control. However, we are using a basic
// NSButton whose initializer does not take an NSCell as an
// object. To honor the assumed semantics, we do nothing with
// NSButton between alloc/init and setCell:.
[self setCell:cell];
+ [cell setTabId:[controller currentTabId]];
+ [cell setExtensionAction:extension->browser_action()];
+
[self setTitle:@""];
[self setButtonType:NSMomentaryChangeButton];
[self setShowsBorderOnlyWhileMouseInside:YES];
@@ -184,16 +191,13 @@ class ExtensionImageTrackerBridge : public NotificationObserver,
[self setTarget:controller];
[self setAction:@selector(browserActionClicked:)];
+ [self setMenu:[[[ExtensionActionContextMenu alloc]
+ initWithExtension:extension] autorelease]];
+
extension_ = extension;
controller_ = controller;
imageLoadingBridge_.reset(new ExtensionImageTrackerBridge(self, extension));
- NSRect badgeFrame = [self bounds];
- badgeView_.reset([[BrowserActionBadgeView alloc] initWithFrame:badgeFrame]);
- [badgeView_ setTabId:[controller currentTabId]];
- [badgeView_ setExtensionAction:extension->browser_action()];
- [self addSubview:badgeView_];
-
[self updateState];
}
@@ -228,7 +232,7 @@ class ExtensionImageTrackerBridge : public NotificationObserver,
[self setImage:defaultIcon_];
}
- [badgeView_ setTabId:tabId];
+ [[self cell] setTabId:tabId];
[self setNeedsDisplay:YES];
}
diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.h b/chrome/browser/cocoa/extensions/extension_action_context_menu.h
new file mode 100644
index 0000000..ffae3ed
--- /dev/null
+++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
+#define CHROME_BROWSER_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
+
+#include "base/ref_counted.h"
+
+#import <Cocoa/Cocoa.h>
+
+class Extension;
+class AsyncUninstaller;
+
+// A context menu used by the Browser and Page Action components that appears
+// if a user right-clicks the view of the given extension.
+@interface ExtensionActionContextMenu : NSMenu {
+ // The extension that this menu belongs to. Weak.
+ Extension* extension_;
+
+ // Used to load the extension icon asynchronously on the I/O thread then show
+ // the uninstall confirmation dialog.
+ scoped_refptr<AsyncUninstaller> uninstaller_;
+}
+
+// Initializes and returns a context menu for the given extension.
+- (id)initWithExtension:(Extension*)extension;
+
+@end
+
+typedef ExtensionActionContextMenu ExtensionActionContextMenuMac;
+
+#endif // CHROME_BROWSER_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm
new file mode 100644
index 0000000..ee3c6ae
--- /dev/null
+++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm
@@ -0,0 +1,195 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/cocoa/extensions/extension_action_context_menu.h"
+
+#include "app/l10n_util_mac.h"
+#include "base/sys_string_conversions.h"
+#include "base/task.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/extensions/extension_install_ui.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/url_constants.h"
+#include "grit/generated_resources.h"
+
+// A class that loads the extension icon on the I/O thread before showing the
+// confirmation dialog to uninstall the given extension.
+// Also acts as the extension's UI delegate in order to display the dialog.
+class AsyncUninstaller : public base::RefCountedThreadSafe<AsyncUninstaller>,
+ public ExtensionInstallUI::Delegate {
+ public:
+ AsyncUninstaller(Extension* extension) : extension_(extension) {}
+
+ // Load the uninstall icon then show the confirmation dialog when finished.
+ void LoadExtensionIconThenConfirmUninstall() {
+ ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &AsyncUninstaller::LoadIconOnFileThread,
+ &uninstall_icon_));
+ }
+
+ void Cancel() {
+ uninstall_icon_.reset();
+ extension_ = NULL;
+ }
+
+ // Overridden by ExtensionInstallUI::Delegate.
+ virtual void InstallUIProceed() {
+ Browser* browser = BrowserList::GetLastActive();
+ // GetLastActive() returns NULL during testing.
+ if (!browser)
+ return;
+ browser->profile()->GetExtensionsService()->
+ UninstallExtension(extension_->id(), false);
+ }
+
+ virtual void InstallUIAbort() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<AsyncUninstaller>;
+ ~AsyncUninstaller() {}
+
+ void LoadIconOnFileThread(scoped_ptr<SkBitmap>* uninstall_icon) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ Extension::DecodeIcon(extension_, Extension::EXTENSION_ICON_LARGE,
+ uninstall_icon);
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &AsyncUninstaller::ShowConfirmationDialog,
+ uninstall_icon));
+ }
+
+ void ShowConfirmationDialog(scoped_ptr<SkBitmap>* uninstall_icon) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // If |extension_| is NULL, then the action was cancelled. Bail.
+ if (!extension_)
+ return;
+
+ Browser* browser = BrowserList::GetLastActive();
+ // GetLastActive() returns NULL during testing.
+ if (!browser)
+ return;
+
+ ExtensionInstallUI client(browser->profile());
+ client.ConfirmUninstall(this, extension_, uninstall_icon->get());
+ }
+
+ // The extension that we're loading the icon for. Weak.
+ Extension* extension_;
+
+ // The uninstall icon shown by the confirmation dialog.
+ scoped_ptr<SkBitmap> uninstall_icon_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncUninstaller);
+};
+
+@interface ExtensionActionContextMenu(Private)
+// Callback for the context menu items.
+- (void)dispatch:(id)menuItem;
+@end
+
+@implementation ExtensionActionContextMenu
+
+namespace {
+// Enum of menu item choices to their respective indices.
+// NOTE: You MUST keep this in sync with the |menuItems| NSArray below.
+enum {
+ kExtensionContextName = 0,
+ kExtensionContextOptions = 2,
+ kExtensionContextDisable = 3,
+ kExtensionContextUninstall = 4,
+ kExtensionContextManage = 6
+};
+} // namespace
+
+- (id)initWithExtension:(Extension*)extension {
+ if ((self = [super initWithTitle:@""])) {
+ extension_ = extension;
+
+ NSArray* menuItems = [NSArray arrayWithObjects:
+ base::SysUTF8ToNSString(extension->name()),
+ [NSMenuItem separatorItem],
+ l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_OPTIONS),
+ l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_DISABLE),
+ l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_UNINSTALL),
+ [NSMenuItem separatorItem],
+ l10n_util::GetNSStringWithFixup(IDS_MANAGE_EXTENSIONS),
+ nil];
+
+ for (id item in menuItems) {
+ if ([item isKindOfClass:[NSMenuItem class]]) {
+ [self addItem:item];
+ } else if ([item isKindOfClass:[NSString class]]) {
+ NSMenuItem* itemObj = [self addItemWithTitle:item
+ action:@selector(dispatch:)
+ keyEquivalent:@""];
+ // The tag should correspond to the enum above.
+ // NOTE: The enum and the order of the menu items MUST be in sync.
+ [itemObj setTag:[self indexOfItem:itemObj]];
+
+ // Disable the 'Options' item if there are no options to set.
+ if ([itemObj tag] == kExtensionContextOptions &&
+ extension_->options_url().spec().length() <= 0) {
+ // Setting the target to nil will disable the item. For some reason
+ // setDisabled:NO does not work.
+ [itemObj setTarget:nil];
+ } else {
+ [itemObj setTarget:self];
+ }
+ }
+ }
+
+ return self;
+ }
+ return nil;
+}
+
+- (void)dispatch:(id)menuItem {
+ Browser* browser = BrowserList::GetLastActive();
+ // GetLastActive() returns NULL during testing.
+ if (!browser)
+ return;
+
+ Profile* profile = browser->profile();
+
+ NSMenuItem* item = (NSMenuItem*)menuItem;
+ switch ([item tag]) {
+ case kExtensionContextName: {
+ GURL url(std::string(extension_urls::kGalleryBrowsePrefix) +
+ std::string("/detail/") + extension_->id());
+ browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
+ break;
+ }
+ case kExtensionContextOptions: {
+ DCHECK(!extension_->options_url().is_empty());
+ browser->OpenURL(extension_->options_url(), GURL(),
+ NEW_FOREGROUND_TAB, PageTransition::LINK);
+ break;
+ }
+ case kExtensionContextDisable: {
+ ExtensionsService* extension_service = profile->GetExtensionsService();
+ extension_service->DisableExtension(extension_->id());
+ break;
+ }
+ case kExtensionContextUninstall: {
+ if (uninstaller_.get())
+ uninstaller_->Cancel();
+
+ uninstaller_ = new AsyncUninstaller(extension_);
+ uninstaller_->LoadExtensionIconThenConfirmUninstall();
+ break;
+ }
+ case kExtensionContextManage: {
+ browser->OpenURL(GURL(chrome::kChromeUIExtensionsURL), GURL(),
+ NEW_FOREGROUND_TAB, PageTransition::LINK);
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+@end