diff options
author | andybons@chromium.org <andybons@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-13 17:24:48 +0000 |
---|---|---|
committer | andybons@chromium.org <andybons@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-13 17:24:48 +0000 |
commit | e117ef215378e5ff9a3a3cddbe8f4cdb51e648b3 (patch) | |
tree | 55c12355d7ea128df045a5b0f0ce756c1e0f032a | |
parent | 122c2543a156fa2310ecd5f8825ca9b8d0abcdd9 (diff) | |
download | chromium_src-e117ef215378e5ff9a3a3cddbe8f4cdb51e648b3.zip chromium_src-e117ef215378e5ff9a3a3cddbe8f4cdb51e648b3.tar.gz chromium_src-e117ef215378e5ff9a3a3cddbe8f4cdb51e648b3.tar.bz2 |
[Mac] Finish up extension infobar UI implementation.
o One issue under consideration is whether the HTML content of the infobar can take up the entire width. Right now on Windows it does not but with this implementation (and on Linux) it does.
BUG=43168
TEST=ExtensionApiTest.Infobars
Review URL: http://codereview.chromium.org/2973003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52182 0039d316-1c4b-4281-b951-d872f2087c98
7 files changed, 201 insertions, 19 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 1b2f708..920e23f 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -33,7 +33,12 @@ without changes to the corresponding grd file. eadee --> <include name="IDR_DOM_UI2_CSS" file="resources\dom_ui2.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_DOWNLOADS_HTML" file="resources\downloads.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_EXTENSION_DEFAULT_ICON" file="resources\extension_default_icon.png" type="BINDATA" /> - <include name="IDR_EXTENSIONS_INFOBAR_CSS" file="resources\extensions_infobar.css" flattenhtml="true" type="BINDATA" /> + <if expr="os == 'darwin'"> + <include name="IDR_EXTENSIONS_INFOBAR_CSS" file="resources\extensions_infobar_mac.css" flattenhtml="true" type="BINDATA" /> + </if> + <if expr="os != 'darwin'"> + <include name="IDR_EXTENSIONS_INFOBAR_CSS" file="resources\extensions_infobar.css" flattenhtml="true" type="BINDATA" /> + </if> <include name="IDR_EXTENSIONS_TOOLSTRIP_THEME_CSS" file="resources\extensions_toolstrip.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_EXTENSIONS_UI_HTML" file="resources\extensions_ui.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_GAIA_LOGIN_HTML" file="sync\resources\gaia_login.html" flattenhtml="true" type="BINDATA" /> diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.h b/chrome/browser/cocoa/extensions/extension_action_context_menu.h index 52c088e..06c2276 100644 --- a/chrome/browser/cocoa/extensions/extension_action_context_menu.h +++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.h @@ -23,8 +23,7 @@ class DevmodeObserver; } // namespace extension_action_context_menu -// A context menu used by the Browser and Page Action components that appears -// if a user right-clicks the view of the given extension. +// A context menu used by any extension UI components that require it. @interface ExtensionActionContextMenu : NSMenu { @private // The extension that this menu belongs to. Weak. diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm index 1622451..1b954a6 100644 --- a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm +++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm @@ -243,6 +243,7 @@ int CurrentTabId() { (AutocompleteTextField*)locationBar->location_entry()-> GetNativeView(); AutocompleteTextFieldCell* fieldCell = [field autocompleteTextFieldCell]; + DCHECK(action_); NSRect popupRect = [fieldCell pageActionFrameForExtensionAction:action_ inFrame:[field bounds]]; @@ -276,8 +277,8 @@ int CurrentTabId() { } - (BOOL)validateMenuItem:(NSMenuItem*)menuItem { - if([menuItem isEqualTo: inspectorItem_.get()]) { - return (action_->HasPopup(CurrentTabId())); + if([menuItem isEqualTo:inspectorItem_.get()]) { + return action_ && action_->HasPopup(CurrentTabId()); } return YES; } diff --git a/chrome/browser/cocoa/extensions/extension_infobar_controller.h b/chrome/browser/cocoa/extensions/extension_infobar_controller.h index 93b7e57..c1dd730 100644 --- a/chrome/browser/cocoa/extensions/extension_infobar_controller.h +++ b/chrome/browser/cocoa/extensions/extension_infobar_controller.h @@ -9,12 +9,30 @@ #import <Cocoa/Cocoa.h> +#import "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" + +@class ExtensionActionContextMenu; +class ExtensionInfoBarDelegate; +class InfobarBridge; +@class MenuButton; + @interface ExtensionInfoBarController : InfoBarController { // The native extension view retrieved from the extension host. Weak. NSView* extensionView_; // The window containing this InfoBar. Weak. NSWindow* window_; + + // The InfoBar's button with the Extension's icon that launches the context + // menu. + scoped_nsobject<MenuButton> dropdownButton_; + + // The context menu that pops up when the left button is clicked. + scoped_nsobject<ExtensionActionContextMenu> contextMenu_; + + // Helper class to bridge C++ and ObjC functionality together for the infobar. + scoped_ptr<InfobarBridge> bridge_; } @end diff --git a/chrome/browser/cocoa/extensions/extension_infobar_controller.mm b/chrome/browser/cocoa/extensions/extension_infobar_controller.mm index 1a65161..f9599fa 100644 --- a/chrome/browser/cocoa/extensions/extension_infobar_controller.mm +++ b/chrome/browser/cocoa/extensions/extension_infobar_controller.mm @@ -4,30 +4,155 @@ #import "chrome/browser/cocoa/extensions/extension_infobar_controller.h" +#include <cmath> + +#include "app/resource_bundle.h" #import "chrome/browser/cocoa/animatable_view.h" +#import "chrome/browser/cocoa/extensions/extension_action_context_menu.h" +#import "chrome/browser/cocoa/menu_button.h" #include "chrome/browser/cocoa/infobar.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_infobar_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_resource.h" +#include "gfx/canvas_skia.h" +#include "grit/theme_resources.h" +#include "skia/ext/skia_utils_mac.h" namespace { const CGFloat kAnimationDuration = 0.12; const CGFloat kBottomBorderHeightPx = 1.0; -} // namepsace +const CGFloat kButtonHeightPx = 26.0; +const CGFloat kButtonLeftMarginPx = 2.0; +const CGFloat kButtonWidthPx = 34.0; +const CGFloat kDropArrowLeftMarginPx = 3.0; +const CGFloat kToolbarMinHeightPx = 36.0; +const CGFloat kToolbarMaxHeightPx = 72.0; +} // namespace @interface ExtensionInfoBarController(Private) // Called when the extension's hosted NSView has been resized. - (void)extensionViewFrameChanged; -// Adjusts the width of the extension's hosted view to match the window's width. -- (void)adjustWidthToFitWindow; +// Returns the clamped height of the extension view to be within the min and max +// values defined above. +- (CGFloat)clampedExtensionViewHeight; +// Adjusts the width of the extension's hosted view to match the window's width +// and sets the proper height for it as well. +- (void)adjustExtensionViewSize; +// Sets the image to be used in the button on the left side of the infobar. +- (void)setButtonImage:(NSImage*)image; @end +// A helper class to bridge the asynchronous Skia bitmap loading mechanism to +// the extension's button. +class InfobarBridge : public ExtensionInfoBarDelegate::DelegateObserver, + public ImageLoadingTracker::Observer { + public: + explicit InfobarBridge(ExtensionInfoBarController* owner) + : owner_(owner), + delegate_([owner delegate]->AsExtensionInfoBarDelegate()), + tracker_(this) { + delegate_->set_observer(this); + LoadIcon(); + } + + virtual ~InfobarBridge() { + if (delegate_) + delegate_->set_observer(NULL); + } + + // Load the Extension's icon image. + void LoadIcon() { + ExtensionResource icon_resource; + Extension* extension = delegate_->extension_host()->extension(); + Extension::Icons size = + extension->GetIconPathAllowLargerSize(&icon_resource, + Extension::EXTENSION_ICON_BITTY); + if (!icon_resource.relative_path().empty()) { + tracker_.LoadImage(extension, icon_resource, gfx::Size(size, size), + ImageLoadingTracker::DONT_CACHE); + } else { + OnImageLoaded(NULL, icon_resource, 0); + } + } + + // ImageLoadingTracker::Observer implementation. + // TODO(andybons): The infobar view implementations share a lot of the same + // code. Come up with a strategy to share amongst them. + virtual void OnImageLoaded( + SkBitmap* image, ExtensionResource resource, int index) { + if (!delegate_) + return; // The delegate can go away while the image asynchronously loads. + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + // Fall back on the default extension icon on failure. + SkBitmap* icon; + if (!image || image->empty()) + icon = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); + else + icon = image; + + SkBitmap* drop_image = rb.GetBitmapNamed(IDR_APP_DROPARROW); + + const int image_size = Extension::EXTENSION_ICON_BITTY; + scoped_ptr<gfx::CanvasSkia> canvas( + new gfx::CanvasSkia( + image_size + kDropArrowLeftMarginPx + drop_image->width(), + image_size, false)); + canvas->DrawBitmapInt(*icon, + 0, 0, icon->width(), icon->height(), + 0, 0, image_size, image_size, + false); + canvas->DrawBitmapInt(*drop_image, + image_size + kDropArrowLeftMarginPx, + image_size / 2); + [owner_ setButtonImage:gfx::SkBitmapToNSImage(canvas->ExtractBitmap())]; + } + + // Overridden from ExtensionInfoBarDelegate::DelegateObserver: + virtual void OnDelegateDeleted() { + delegate_ = NULL; + } + + private: + // Weak. Owns us. + ExtensionInfoBarController* owner_; + + // Weak. + ExtensionInfoBarDelegate* delegate_; + + // Loads the extensions's icon on the file thread. + ImageLoadingTracker tracker_; + + DISALLOW_COPY_AND_ASSIGN(InfobarBridge); +}; + + @implementation ExtensionInfoBarController - (id)initWithDelegate:(InfoBarDelegate*)delegate window:(NSWindow*)window { if ((self = [super initWithDelegate:delegate])) { window_ = window; + dropdownButton_.reset([[MenuButton alloc] init]); + + ExtensionHost* extensionHost = delegate_->AsExtensionInfoBarDelegate()-> + extension_host(); + contextMenu_.reset([[ExtensionActionContextMenu alloc] + initWithExtension:extensionHost->extension() + profile:extensionHost->profile() + extensionAction:NULL]); + // See menu_button.h for documentation on why this is needed. + NSMenuItem* dummyItem = + [[[NSMenuItem alloc] initWithTitle:@"" + action:nil + keyEquivalent:@""] autorelease]; + [contextMenu_ insertItem:dummyItem atIndex:0]; + [dropdownButton_ setAttachedMenu:contextMenu_.get()]; + + bridge_.reset(new InfobarBridge(self)); } return self; } @@ -40,8 +165,8 @@ const CGFloat kBottomBorderHeightPx = 1.0; - (void)addAdditionalControls { [self removeButtons]; - extensionView_ = delegate_->AsExtensionInfoBarDelegate()-> - extension_host()->view()->native_view(); + extensionView_ = delegate_->AsExtensionInfoBarDelegate()->extension_host()-> + view()->native_view(); // Add the extension's RenderWidgetHostViewMac to the view hierarchy of the // InfoBar and make sure to place it below the Close button. @@ -49,6 +174,17 @@ const CGFloat kBottomBorderHeightPx = 1.0; positioned:NSWindowBelow relativeTo:(NSView*)closeButton_]; + // Add the context menu button to the hierarchy. + [dropdownButton_ setShowsBorderOnlyWhileMouseInside:YES]; + CGFloat buttonY = + std::floor(NSMidY([infoBarView_ frame]) - (kButtonHeightPx / 2.0)) + + kBottomBorderHeightPx; + NSRect buttonFrame = NSMakeRect( + kButtonLeftMarginPx, buttonY, kButtonWidthPx, kButtonHeightPx); + [dropdownButton_ setFrame:buttonFrame]; + [dropdownButton_ setAutoresizingMask:NSViewMinYMargin | NSViewMaxYMargin]; + [infoBarView_ addSubview:dropdownButton_]; + // Because the parent view has a bottom border, account for it during // positioning. NSRect extensionFrame = [extensionView_ frame]; @@ -61,14 +197,15 @@ const CGFloat kBottomBorderHeightPx = 1.0; // needed is because the extension view's frame will not have changed in the // above case, so the NSViewFrameDidChangeNotification registered below will // never fire. - if (extensionFrame.size.height > 0.0) { + if (NSHeight(extensionFrame) > 0.0) { NSSize infoBarSize = [[self view] frame].size; - infoBarSize.height = extensionFrame.size.height + kBottomBorderHeightPx; + infoBarSize.height = [self clampedExtensionViewHeight] + + kBottomBorderHeightPx; [[self view] setFrameSize:infoBarSize]; [infoBarView_ setFrameSize:infoBarSize]; } - [self adjustWidthToFitWindow]; + [self adjustExtensionViewSize]; // These two notification handlers are here to ensure the width of the // native extension view is the same as the browser window's width and that @@ -87,11 +224,11 @@ const CGFloat kBottomBorderHeightPx = 1.0; } - (void)extensionViewFrameChanged { - [self adjustWidthToFitWindow]; + [self adjustExtensionViewSize]; AnimatableView* view = [self animatableView]; NSRect infoBarFrame = [view frame]; - CGFloat newHeight = NSHeight([extensionView_ frame]) + kBottomBorderHeightPx; + CGFloat newHeight = [self clampedExtensionViewHeight] + kBottomBorderHeightPx; [infoBarView_ setPostsFrameChangedNotifications:NO]; infoBarFrame.size.height = newHeight; [infoBarView_ setFrame:infoBarFrame]; @@ -99,14 +236,24 @@ const CGFloat kBottomBorderHeightPx = 1.0; [view animateToNewHeight:newHeight duration:kAnimationDuration]; } -- (void)adjustWidthToFitWindow { +- (CGFloat)clampedExtensionViewHeight { + return std::max(kToolbarMinHeightPx, + std::min(NSHeight([extensionView_ frame]), kToolbarMaxHeightPx)); +} + +- (void)adjustExtensionViewSize { [extensionView_ setPostsFrameChangedNotifications:NO]; NSSize extensionViewSize = [extensionView_ frame].size; extensionViewSize.width = NSWidth([window_ frame]); + extensionViewSize.height = [self clampedExtensionViewHeight]; [extensionView_ setFrameSize:extensionViewSize]; [extensionView_ setPostsFrameChangedNotifications:YES]; } +- (void)setButtonImage:(NSImage*)image { + [dropdownButton_ setImage:image]; +} + @end InfoBar* ExtensionInfoBarDelegate::CreateInfoBar() { diff --git a/chrome/browser/extensions/extension_infobar_apitest.cc b/chrome/browser/extensions/extension_infobar_apitest.cc index 47f726e..a7afb17 100644 --- a/chrome/browser/extensions/extension_infobar_apitest.cc +++ b/chrome/browser/extensions/extension_infobar_apitest.cc @@ -6,11 +6,10 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" -#if defined(TOOLKIT_VIEWS) +#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) #define MAYBE_Infobars Infobars #else -// Need to port ExtensionInfoBarDelegate::CreateInfoBar() to other platforms. -// See http://crbug.com/39916 for details. +// Need to finish port to Linux. See http://crbug.com/39916 for details. #define MAYBE_Infobars DISABLED_Infobars #endif diff --git a/chrome/browser/resources/extensions_infobar_mac.css b/chrome/browser/resources/extensions_infobar_mac.css new file mode 100644 index 0000000..c2a9bbe --- /dev/null +++ b/chrome/browser/resources/extensions_infobar_mac.css @@ -0,0 +1,13 @@ +/** + * The following style rules affect Extension Infobars on the Mac. + */ + +body { + background: -webkit-gradient(linear, left top, left bottom, + from(#EBEBEB), to(#CFCFCF)); + font-family: 'Lucida Grande', Helvetica, sans-serif; + font-size: 11px; + height: 36px; /* Infobars are limited to 36-72px */ + margin: 0px; + overflow: hidden; +} |