| Index: chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
|
| index f68a15e51410d9eab97fb3c0cf067861a4d5c7f3..f6110909f574fada879408d05fa3e1e55e9c06d5 100644
|
| --- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
|
| +++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
|
| @@ -3,81 +3,25 @@
|
| // found in the LICENSE file.
|
|
|
| #include "base/logging.h"
|
| -#include "base/mac/bundle_locations.h"
|
| #include "base/mac/mac_util.h"
|
| -#include "chrome/browser/chrome_notification_types.h"
|
| +#include "base/message_loop/message_loop.h"
|
| #include "chrome/browser/infobars/confirm_infobar_delegate.h"
|
| #include "chrome/browser/infobars/infobar.h"
|
| +#include "chrome/browser/infobars/infobar_container.h"
|
| #include "chrome/browser/infobars/infobar_service.h"
|
| -#import "chrome/browser/ui/cocoa/animatable_view.h"
|
| #import "chrome/browser/ui/cocoa/browser_window_controller.h"
|
| +#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
|
| +#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
|
| #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
|
| #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
|
| +#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
|
| #import "chrome/browser/ui/cocoa/view_id_util.h"
|
| -#include "content/public/browser/notification_details.h"
|
| -#include "content/public/browser/notification_source.h"
|
| -#include "skia/ext/skia_utils_mac.h"
|
| -
|
| -// C++ class that receives INFOBAR_ADDED and INFOBAR_REMOVED
|
| -// notifications and proxies them back to |controller|.
|
| -class InfoBarNotificationObserver : public content::NotificationObserver {
|
| - public:
|
| - InfoBarNotificationObserver(InfoBarContainerController* controller)
|
| - : controller_(controller) {
|
| - }
|
| -
|
| - private:
|
| - // NotificationObserver implementation
|
| - virtual void Observe(
|
| - int type,
|
| - const content::NotificationSource& source,
|
| - const content::NotificationDetails& details) OVERRIDE {
|
| - InfoBarService* infobar_service =
|
| - content::Source<InfoBarService>(source).ptr();
|
| - switch (type) {
|
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED:
|
| - [controller_ addInfoBar:content::Details<InfoBarAddedDetails>(details)->
|
| - CreateInfoBar(infobar_service)
|
| - animate:YES];
|
| - break;
|
| -
|
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED: {
|
| - InfoBarRemovedDetails* removed_details =
|
| - content::Details<InfoBarRemovedDetails>(details).ptr();
|
| - [controller_
|
| - closeInfoBarsForDelegate:removed_details->first
|
| - animate:(removed_details->second ? YES : NO)];
|
| - break;
|
| - }
|
| -
|
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED: {
|
| - InfoBarReplacedDetails* replaced_details =
|
| - content::Details<InfoBarReplacedDetails>(details).ptr();
|
| - [controller_ closeInfoBarsForDelegate:replaced_details->first
|
| - animate:NO];
|
| - [controller_ addInfoBar:replaced_details->second->
|
| - CreateInfoBar(infobar_service)
|
| - animate:NO];
|
| - break;
|
| - }
|
| -
|
| - default:
|
| - NOTREACHED(); // we don't ask for anything else!
|
| - break;
|
| - }
|
| -
|
| - [controller_ positionInfoBarsAndRedraw];
|
| - }
|
| -
|
| - InfoBarContainerController* controller_; // weak, owns us.
|
| -};
|
| -
|
| -
|
| -@interface InfoBarContainerController (PrivateMethods)
|
| -// Returns the desired height of the container view, computed by
|
| -// adding together the heights of all its subviews.
|
| -- (CGFloat)desiredHeight;
|
|
|
| +@interface InfoBarContainerController ()
|
| +// Removes |controller| from the list of controllers in this container and
|
| +// removes its view from the view hierarchy. This method is safe to call while
|
| +// |controller| is still on the call stack.
|
| +- (void)removeController:(InfoBarController*)controller;
|
| @end
|
|
|
|
|
| @@ -87,50 +31,28 @@ class InfoBarNotificationObserver : public content::NotificationObserver {
|
|
|
| - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate {
|
| DCHECK(resizeDelegate);
|
| - if ((self = [super initWithNibName:@"InfoBarContainer"
|
| - bundle:base::mac::FrameworkBundle()])) {
|
| - resizeDelegate_ = resizeDelegate;
|
| - infoBarObserver_.reset(new InfoBarNotificationObserver(self));
|
| + if ((self = [super initWithNibName:nil bundle:nil])) {
|
| + base::scoped_nsobject<NSView> view(
|
| + [[NSView alloc] initWithFrame:NSZeroRect]);
|
| + [view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
|
| + view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER);
|
| + [self setView:view];
|
|
|
| - // NSMutableArray needs an initial capacity, and we rarely ever see
|
| - // more than two infobars at a time, so that seems like a good choice.
|
| - infobarControllers_.reset([[NSMutableArray alloc] initWithCapacity:2]);
|
| - closingInfoBars_.reset([[NSMutableSet alloc] initWithCapacity:2]);
|
| + resizeDelegate_ = resizeDelegate;
|
| + containerCocoa_.reset(new InfoBarContainerCocoa(self));
|
| + infobarControllers_.reset([[NSMutableArray alloc] init]);
|
| }
|
| return self;
|
| }
|
|
|
| - (void)dealloc {
|
| + // Delete the container so that any remaining infobars are removed.
|
| + containerCocoa_.reset();
|
| DCHECK_EQ([infobarControllers_ count], 0U);
|
| - DCHECK_EQ([closingInfoBars_ count], 0U);
|
| view_id_util::UnsetID([self view]);
|
| [super dealloc];
|
| }
|
|
|
| -- (void)awakeFromNib {
|
| - // The info bar container view is an ordinary NSView object, so we set its
|
| - // ViewID here.
|
| - view_id_util::SetID([self view], VIEW_ID_INFO_BAR_CONTAINER);
|
| -}
|
| -
|
| -- (void)willRemoveController:(InfoBarController*)controller {
|
| - [closingInfoBars_ addObject:controller];
|
| -}
|
| -
|
| -- (void)removeController:(InfoBarController*)controller {
|
| - if (![infobarControllers_ containsObject:controller])
|
| - return;
|
| -
|
| - // This code can be executed while InfoBarController is still on the stack, so
|
| - // we retain and autorelease the controller to prevent it from being
|
| - // dealloc'ed too early.
|
| - [[controller retain] autorelease];
|
| - [[controller view] removeFromSuperview];
|
| - [infobarControllers_ removeObject:controller];
|
| - [closingInfoBars_ removeObject:controller];
|
| - [self positionInfoBarsAndRedraw];
|
| -}
|
| -
|
| - (BrowserWindowController*)browserWindowController {
|
| id controller = [[[self view] window] windowController];
|
| if (![controller isKindOfClass:[BrowserWindowController class]])
|
| @@ -138,30 +60,19 @@ class InfoBarNotificationObserver : public content::NotificationObserver {
|
| return controller;
|
| }
|
|
|
| -- (void)changeWebContents:(content::WebContents*)contents {
|
| - registrar_.RemoveAll();
|
| - [self removeAllInfoBars];
|
| -
|
| - currentWebContents_ = contents;
|
| - if (currentWebContents_) {
|
| - InfoBarService* infobarService =
|
| - InfoBarService::FromWebContents(currentWebContents_);
|
| - for (size_t i = 0; i < infobarService->infobar_count(); ++i) {
|
| - InfoBar* infobar =
|
| - infobarService->infobar_at(i)->CreateInfoBar(infobarService);
|
| - [self addInfoBar:infobar animate:NO];
|
| - }
|
| +- (CGFloat)infobarArrowX {
|
| + LocationBarViewMac* locationBar =
|
| + [[self browserWindowController] locationBarBridge];
|
| + return locationBar->GetPageInfoBubblePoint().x;
|
| +}
|
|
|
| - content::Source<InfoBarService> source(infobarService);
|
| - registrar_.Add(infoBarObserver_.get(),
|
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, source);
|
| - registrar_.Add(infoBarObserver_.get(),
|
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, source);
|
| - registrar_.Add(infoBarObserver_.get(),
|
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED, source);
|
| +- (void)changeWebContents:(content::WebContents*)contents {
|
| + if (contents) {
|
| + containerCocoa_->ChangeInfoBarService(
|
| + InfoBarService::FromWebContents(contents));
|
| + } else {
|
| + containerCocoa_->ChangeInfoBarService(NULL);
|
| }
|
| -
|
| - [self positionInfoBarsAndRedraw];
|
| }
|
|
|
| - (void)tabDetachedWithContents:(content::WebContents*)contents {
|
| @@ -169,99 +80,37 @@ class InfoBarNotificationObserver : public content::NotificationObserver {
|
| [self changeWebContents:NULL];
|
| }
|
|
|
| -- (NSUInteger)infobarCount {
|
| - return [infobarControllers_ count] - [closingInfoBars_ count];
|
| -}
|
| -
|
| - (CGFloat)overlappingTipHeight {
|
| - return [self infobarCount] ? infobars::kTipHeight : 0;
|
| -}
|
| -
|
| -- (void)resizeView:(NSView*)view newHeight:(CGFloat)height {
|
| - NSRect frame = [view frame];
|
| - frame.size.height = height;
|
| - [view setFrame:frame];
|
| - [self positionInfoBarsAndRedraw];
|
| -}
|
| -
|
| -- (void)setAnimationInProgress:(BOOL)inProgress {
|
| - if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
|
| - [resizeDelegate_ setAnimationInProgress:inProgress];
|
| -}
|
| -
|
| -- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
|
| - if (shouldSuppressTopInfoBarTip_ == flag)
|
| - return;
|
| - shouldSuppressTopInfoBarTip_ = flag;
|
| - [self positionInfoBarsAndRedraw];
|
| -}
|
| -
|
| -@end
|
| -
|
| -@implementation InfoBarContainerController (PrivateMethods)
|
| -
|
| -- (CGFloat)desiredHeight {
|
| - CGFloat height = 0;
|
| -
|
| - // Take out the height of the tip from the total size of the infobar so that
|
| - // the tip overlaps the preceding infobar when there is more than one infobar.
|
| - for (InfoBarController* controller in infobarControllers_.get())
|
| - height += NSHeight([[controller view] frame]) - infobars::kTipHeight;
|
| -
|
| - // If there are any infobars, add a little extra room for the tip of the first
|
| - // infobar.
|
| - if (height)
|
| - height += infobars::kTipHeight;
|
| -
|
| - return height;
|
| + return containerCocoa_->GetVerticalOverlap(NULL);
|
| }
|
|
|
| -- (void)addInfoBar:(InfoBar*)infobar animate:(BOOL)animate {
|
| +- (void)addInfoBar:(InfoBarCocoa*)infobar
|
| + position:(NSUInteger)position {
|
| InfoBarController* controller = infobar->controller();
|
| [controller setContainerController:self];
|
| - [[controller animatableView] setResizeDelegate:self];
|
| - [[self view] addSubview:[controller view]];
|
| - [infobarControllers_ addObject:[controller autorelease]];
|
| + [infobarControllers_ insertObject:controller atIndex:position];
|
|
|
| - if (animate)
|
| - [controller animateOpen];
|
| - else
|
| - [controller open];
|
| -
|
| - delete infobar;
|
| + NSView* relativeView = nil;
|
| + if (position > 0)
|
| + relativeView = [[infobarControllers_ objectAtIndex:position - 1] view];
|
| + [[self view] addSubview:[controller view]
|
| + positioned:NSWindowAbove
|
| + relativeTo:relativeView];
|
| }
|
|
|
| -- (void)closeInfoBarsForDelegate:(InfoBarDelegate*)delegate
|
| - animate:(BOOL)animate {
|
| - for (InfoBarController* controller in
|
| - [NSArray arrayWithArray:infobarControllers_.get()]) {
|
| - if ([controller delegate] == delegate) {
|
| - [controller infobarWillClose];
|
| - if (animate)
|
| - [controller animateClosed];
|
| - else
|
| - [controller close];
|
| - }
|
| - }
|
| +- (void)removeInfoBar:(InfoBarCocoa*)infobar {
|
| + [infobar->controller() infobarWillClose];
|
| + [self removeController:infobar->controller()];
|
| + base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar);
|
| }
|
|
|
| -- (void)removeAllInfoBars {
|
| - // stopAnimation can remove the infobar from infobarControllers_ if it was in
|
| - // the midst of closing, so copy the array so mutations won't cause problems.
|
| - for (InfoBarController* controller in
|
| - [NSArray arrayWithArray:infobarControllers_.get()]) {
|
| - [[controller animatableView] stopAnimation];
|
| - // This code can be executed while InfoBarController is still on the stack,
|
| - // so we retain and autorelease the controller to prevent it from being
|
| - // dealloc'ed too early.
|
| - [[controller retain] autorelease];
|
| - [[controller view] removeFromSuperview];
|
| +- (void)positionInfoBarsAndRedraw:(BOOL)isAnimating {
|
| + if (isAnimating_ != isAnimating) {
|
| + isAnimating_ = isAnimating;
|
| + if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
|
| + [resizeDelegate_ setAnimationInProgress:isAnimating_];
|
| }
|
| - [infobarControllers_ removeAllObjects];
|
| - [closingInfoBars_ removeAllObjects];
|
| -}
|
|
|
| -- (void)positionInfoBarsAndRedraw {
|
| NSRect containerBounds = [[self view] bounds];
|
| int minY = 0;
|
|
|
| @@ -271,20 +120,47 @@ class InfoBarNotificationObserver : public content::NotificationObserver {
|
| // the others below.
|
| for (InfoBarController* controller in
|
| [infobarControllers_ reverseObjectEnumerator]) {
|
| - NSView* view = [controller view];
|
| - NSRect frame = [view frame];
|
| + NSRect frame;
|
| frame.origin.x = NSMinX(containerBounds);
|
| frame.origin.y = minY;
|
| frame.size.width = NSWidth(containerBounds);
|
| - [view setFrame:frame];
|
| + frame.size.height = [controller infobar]->total_height();
|
| + [[controller view] setFrame:frame];
|
|
|
| - minY += NSHeight(frame) - infobars::kTipHeight;
|
| + minY += NSHeight(frame) - [controller infobar]->arrow_height();
|
| + [controller layoutArrow];
|
| + }
|
| +
|
| + int totalHeight = 0;
|
| + int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight);
|
|
|
| - BOOL isTop = [controller isEqual:[infobarControllers_ objectAtIndex:0]];
|
| - [controller setHasTip:!shouldSuppressTopInfoBarTip_ || !isTop];
|
| + if (NSHeight([[self view] frame]) != totalHeight) {
|
| + [resizeDelegate_ resizeView:[self view] newHeight:totalHeight];
|
| + } else if (oldOverlappingTipHeight_ != overlap) {
|
| + // If the infobar overlap changed but the height didn't change then
|
| + // explicitly ask for a layout.
|
| + [[self browserWindowController] layoutInfoBars];
|
| }
|
| + oldOverlappingTipHeight_ = overlap;
|
| +}
|
| +
|
| +- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
|
| + if (shouldSuppressTopInfoBarTip_ == flag)
|
| + return;
|
| + shouldSuppressTopInfoBarTip_ = flag;
|
| + [self positionInfoBarsAndRedraw:isAnimating_];
|
| +}
|
|
|
| - [resizeDelegate_ resizeView:[self view] newHeight:[self desiredHeight]];
|
| +- (void)removeController:(InfoBarController*)controller {
|
| + if (![infobarControllers_ containsObject:controller])
|
| + return;
|
| +
|
| + // This code can be executed while InfoBarController is still on the stack, so
|
| + // we retain and autorelease the controller to prevent it from being
|
| + // dealloc'ed too early.
|
| + [[controller retain] autorelease];
|
| + [[controller view] removeFromSuperview];
|
| + [infobarControllers_ removeObject:controller];
|
| }
|
|
|
| @end
|
|
|