| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
| 6 | 6 |
| 7 #include "base/logging.h" // for NOTREACHED() | 7 #include "base/logging.h" // for NOTREACHED() |
| 8 #include "base/mac/mac_util.h" | 8 #include "base/mac/mac_util.h" |
| 9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/browser/infobars/infobar_tab_helper.h" | 10 #include "chrome/browser/infobars/infobar_tab_helper.h" |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 namespace { | 27 namespace { |
| 28 // Durations set to match the default SlideAnimation duration. | 28 // Durations set to match the default SlideAnimation duration. |
| 29 const float kAnimateOpenDuration = 0.12; | 29 const float kAnimateOpenDuration = 0.12; |
| 30 const float kAnimateCloseDuration = 0.12; | 30 const float kAnimateCloseDuration = 0.12; |
| 31 } | 31 } |
| 32 | 32 |
| 33 @interface InfoBarController (PrivateMethods) | 33 @interface InfoBarController (PrivateMethods) |
| 34 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. | 34 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. |
| 35 - (void)initializeLabel; | 35 - (void)initializeLabel; |
| 36 | 36 |
| 37 // Asks the container controller to remove the infobar for this delegate. This | |
| 38 // call will trigger a notification that starts the infobar animating closed. | |
| 39 - (void)removeSelf; | |
| 40 | |
| 41 // Performs final cleanup after an animation is finished or stopped, including | 37 // Performs final cleanup after an animation is finished or stopped, including |
| 42 // notifying the InfoBarDelegate that the infobar was closed and removing the | 38 // notifying the InfoBarDelegate that the infobar was closed and removing the |
| 43 // infobar from its container, if necessary. | 39 // infobar from its container, if necessary. |
| 44 - (void)cleanUpAfterAnimation:(BOOL)finished; | 40 - (void)cleanUpAfterAnimation:(BOOL)finished; |
| 45 | 41 |
| 46 // Returns the point, in gradient view coordinates, at which the apex of the | 42 // Returns the point, in gradient view coordinates, at which the apex of the |
| 47 // infobar tip should be drawn. | 43 // infobar tip should be drawn. |
| 48 - (NSPoint)pointForTipApex; | 44 - (NSPoint)pointForTipApex; |
| 49 @end | 45 @end |
| 50 | 46 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 | 94 |
| 99 // Called when someone clicks on the embedded link. | 95 // Called when someone clicks on the embedded link. |
| 100 - (BOOL) textView:(NSTextView*)textView | 96 - (BOOL) textView:(NSTextView*)textView |
| 101 clickedOnLink:(id)link | 97 clickedOnLink:(id)link |
| 102 atIndex:(NSUInteger)charIndex { | 98 atIndex:(NSUInteger)charIndex { |
| 103 if ([self respondsToSelector:@selector(linkClicked)]) | 99 if ([self respondsToSelector:@selector(linkClicked)]) |
| 104 [self performSelector:@selector(linkClicked)]; | 100 [self performSelector:@selector(linkClicked)]; |
| 105 return YES; | 101 return YES; |
| 106 } | 102 } |
| 107 | 103 |
| 104 - (BOOL)isOwned { |
| 105 return !!owner_; |
| 106 } |
| 107 |
| 108 // Called when someone clicks on the ok button. | 108 // Called when someone clicks on the ok button. |
| 109 - (void)ok:(id)sender { | 109 - (void)ok:(id)sender { |
| 110 // Subclasses must override this method if they do not hide the ok button. | 110 // Subclasses must override this method if they do not hide the ok button. |
| 111 NOTREACHED(); | 111 NOTREACHED(); |
| 112 } | 112 } |
| 113 | 113 |
| 114 // Called when someone clicks on the cancel button. | 114 // Called when someone clicks on the cancel button. |
| 115 - (void)cancel:(id)sender { | 115 - (void)cancel:(id)sender { |
| 116 // Subclasses must override this method if they do not hide the cancel button. | 116 // Subclasses must override this method if they do not hide the cancel button. |
| 117 NOTREACHED(); | 117 NOTREACHED(); |
| 118 } | 118 } |
| 119 | 119 |
| 120 // Called when someone clicks on the close button. | 120 // Called when someone clicks on the close button. |
| 121 - (void)dismiss:(id)sender { | 121 - (void)dismiss:(id)sender { |
| 122 if (delegate_) | 122 if (![self isOwned]) |
| 123 delegate_->InfoBarDismissed(); | 123 return; |
| 124 delegate_->InfoBarDismissed(); |
| 125 [self removeSelf]; |
| 126 } |
| 124 | 127 |
| 125 [self removeSelf]; | 128 - (void)removeSelf { |
| 129 DCHECK(owner_); |
| 130 owner_->infobar_tab_helper()->RemoveInfoBar(delegate_); |
| 126 } | 131 } |
| 127 | 132 |
| 128 - (AnimatableView*)animatableView { | 133 - (AnimatableView*)animatableView { |
| 129 return static_cast<AnimatableView*>([self view]); | 134 return static_cast<AnimatableView*>([self view]); |
| 130 } | 135 } |
| 131 | 136 |
| 132 - (void)open { | 137 - (void)open { |
| 133 // Simply reset the frame size to its opened size, forcing a relayout. | 138 // Simply reset the frame size to its opened size, forcing a relayout. |
| 134 CGFloat finalHeight = [[self view] frame].size.height; | 139 CGFloat finalHeight = [[self view] frame].size.height; |
| 135 [[self animatableView] setHeight:finalHeight]; | 140 [[self animatableView] setHeight:finalHeight]; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 158 | 163 |
| 159 // Start animating closed. We will receive a notification when the animation | 164 // Start animating closed. We will receive a notification when the animation |
| 160 // is done, at which point we can remove our view from the hierarchy and | 165 // is done, at which point we can remove our view from the hierarchy and |
| 161 // notify the delegate that the infobar was closed. | 166 // notify the delegate that the infobar was closed. |
| 162 [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration]; | 167 [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration]; |
| 163 | 168 |
| 164 // The above call may trigger an animationDidStop: notification for any | 169 // The above call may trigger an animationDidStop: notification for any |
| 165 // currently-running animations, so do not set |infoBarClosing_| until after | 170 // currently-running animations, so do not set |infoBarClosing_| until after |
| 166 // starting the animation. | 171 // starting the animation. |
| 167 infoBarClosing_ = YES; | 172 infoBarClosing_ = YES; |
| 168 | |
| 169 // The owner called this method to close the infobar, so there will | |
| 170 // be no need to forward future remove events to the owner. | |
| 171 owner_ = NULL; | |
| 172 } | 173 } |
| 173 | 174 |
| 174 - (void)addAdditionalControls { | 175 - (void)addAdditionalControls { |
| 175 // Default implementation does nothing. | 176 // Default implementation does nothing. |
| 176 } | 177 } |
| 177 | 178 |
| 178 - (void)infobarWillClose { | 179 - (void)infobarWillClose { |
| 179 // Default implementation does nothing. | 180 owner_ = NULL; |
| 180 } | 181 } |
| 181 | 182 |
| 182 - (void)removeButtons { | 183 - (void)removeButtons { |
| 183 // Extend the label all the way across. | 184 // Extend the label all the way across. |
| 184 NSRect labelFrame = [label_.get() frame]; | 185 NSRect labelFrame = [label_.get() frame]; |
| 185 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); | 186 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); |
| 186 [okButton_ removeFromSuperview]; | 187 [okButton_ removeFromSuperview]; |
| 187 okButton_ = nil; | 188 okButton_ = nil; |
| 188 [cancelButton_ removeFromSuperview]; | 189 [cancelButton_ removeFromSuperview]; |
| 189 cancelButton_ = nil; | 190 cancelButton_ = nil; |
| 190 [label_.get() setFrame:labelFrame]; | 191 [label_.get() setFrame:labelFrame]; |
| 191 } | 192 } |
| 192 | 193 |
| 194 - (void)disablePopUpMenu:(NSMenu*)menu { |
| 195 // Remove the menu if visible. |
| 196 [menu cancelTracking]; |
| 197 |
| 198 // If the menu is re-opened, prevent queries to update items. |
| 199 [menu setDelegate:nil]; |
| 200 |
| 201 // Prevent target/action messages to the controller. |
| 202 for (NSMenuItem* item in [menu itemArray]) { |
| 203 [item setEnabled:NO]; |
| 204 [item setTarget:nil]; |
| 205 } |
| 206 } |
| 207 |
| 193 @end | 208 @end |
| 194 | 209 |
| 195 @implementation InfoBarController (PrivateMethods) | 210 @implementation InfoBarController (PrivateMethods) |
| 196 | 211 |
| 197 - (void)initializeLabel { | 212 - (void)initializeLabel { |
| 198 // Replace the label placeholder NSTextField with the real label NSTextView. | 213 // Replace the label placeholder NSTextField with the real label NSTextView. |
| 199 // The former doesn't show links in a nice way, but the latter can't be added | 214 // The former doesn't show links in a nice way, but the latter can't be added |
| 200 // in IB without a containing scroll view, so create the NSTextView | 215 // in IB without a containing scroll view, so create the NSTextView |
| 201 // programmatically. | 216 // programmatically. |
| 202 label_.reset([[HyperlinkTextView alloc] | 217 label_.reset([[HyperlinkTextView alloc] |
| 203 initWithFrame:[labelPlaceholder_ frame]]); | 218 initWithFrame:[labelPlaceholder_ frame]]); |
| 204 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; | 219 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; |
| 205 [[labelPlaceholder_ superview] | 220 [[labelPlaceholder_ superview] |
| 206 replaceSubview:labelPlaceholder_ with:label_.get()]; | 221 replaceSubview:labelPlaceholder_ with:label_.get()]; |
| 207 labelPlaceholder_ = nil; // Now released. | 222 labelPlaceholder_ = nil; // Now released. |
| 208 [label_.get() setDelegate:self]; | 223 [label_.get() setDelegate:self]; |
| 209 } | 224 } |
| 210 | 225 |
| 211 - (void)removeSelf { | |
| 212 // TODO(rohitrao): This method can be called even if the infobar has already | |
| 213 // been removed and |delegate_| is NULL. Is there a way to rewrite the code | |
| 214 // so that inner event loops don't cause us to try and remove the infobar | |
| 215 // twice? http://crbug.com/54253 | |
| 216 if (owner_) | |
| 217 owner_->infobar_tab_helper()->RemoveInfoBar(delegate_); | |
| 218 owner_ = NULL; | |
| 219 } | |
| 220 | |
| 221 - (void)cleanUpAfterAnimation:(BOOL)finished { | 226 - (void)cleanUpAfterAnimation:(BOOL)finished { |
| 222 // Don't need to do any cleanup if the bar was animating open. | 227 // Don't need to do any cleanup if the bar was animating open. |
| 223 if (!infoBarClosing_) | 228 if (!infoBarClosing_) |
| 224 return; | 229 return; |
| 225 | 230 |
| 226 // Notify the delegate that the infobar was closed. The delegate will delete | 231 // Notify the delegate that the infobar was closed. The delegate will delete |
| 227 // itself as a result of InfoBarClosed(), so we null out its pointer. | 232 // itself as a result of InfoBarClosed(), so we null out its pointer. |
| 228 if (delegate_) { | 233 if (delegate_) { |
| 229 delegate_->InfoBarClosed(); | 234 delegate_->InfoBarClosed(); |
| 230 delegate_ = NULL; | 235 delegate_ = NULL; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 atOffset:offset | 296 atOffset:offset |
| 292 font:font | 297 font:font |
| 293 messageColor:[NSColor blackColor] | 298 messageColor:[NSColor blackColor] |
| 294 linkColor:[NSColor blueColor]]; | 299 linkColor:[NSColor blueColor]]; |
| 295 } | 300 } |
| 296 | 301 |
| 297 // Called when someone clicks on the link in the infobar. This method | 302 // Called when someone clicks on the link in the infobar. This method |
| 298 // is called by the InfobarTextField on its delegate (the | 303 // is called by the InfobarTextField on its delegate (the |
| 299 // LinkInfoBarController). | 304 // LinkInfoBarController). |
| 300 - (void)linkClicked { | 305 - (void)linkClicked { |
| 306 if (![self isOwned]) |
| 307 return; |
| 301 WindowOpenDisposition disposition = | 308 WindowOpenDisposition disposition = |
| 302 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 309 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
| 303 if (delegate_ && | 310 if (delegate_->AsLinkInfoBarDelegate()->LinkClicked(disposition)) |
| 304 delegate_->AsLinkInfoBarDelegate()->LinkClicked(disposition)) { | 311 [self removeSelf]; |
| 305 // Call |-removeSelf| on the outermost runloop to force a delay. As shown in | |
| 306 // <http://crbug.com/87201>, the second click event can be delivered after | |
| 307 // the animation finishes (and this gets released and deallocated), which | |
| 308 // leads to zombie messaging. Unfortunately, the order between the animation | |
| 309 // finishing and the click event being delivered is nondeterministic, so | |
| 310 // this hack is the best that can be done. | |
| 311 [self performSelector:@selector(removeSelf) | |
| 312 withObject:nil | |
| 313 afterDelay:0.0]; | |
| 314 } | |
| 315 } | 312 } |
| 316 | 313 |
| 317 @end | 314 @end |
| 318 | 315 |
| 319 | 316 |
| 320 ///////////////////////////////////////////////////////////////////////// | 317 ///////////////////////////////////////////////////////////////////////// |
| 321 // ConfirmInfoBarController implementation | 318 // ConfirmInfoBarController implementation |
| 322 | 319 |
| 323 @implementation ConfirmInfoBarController | 320 @implementation ConfirmInfoBarController |
| 324 | 321 |
| 325 // Called when someone clicks on the "OK" button. | 322 // Called when someone clicks on the "OK" button. |
| 326 - (IBAction)ok:(id)sender { | 323 - (IBAction)ok:(id)sender { |
| 327 if (delegate_ && delegate_->AsConfirmInfoBarDelegate()->Accept()) | 324 if (![self isOwned]) |
| 325 return; |
| 326 if (delegate_->AsConfirmInfoBarDelegate()->Accept()) |
| 328 [self removeSelf]; | 327 [self removeSelf]; |
| 329 } | 328 } |
| 330 | 329 |
| 331 // Called when someone clicks on the "Cancel" button. | 330 // Called when someone clicks on the "Cancel" button. |
| 332 - (IBAction)cancel:(id)sender { | 331 - (IBAction)cancel:(id)sender { |
| 333 if (delegate_ && delegate_->AsConfirmInfoBarDelegate()->Cancel()) | 332 if (![self isOwned]) |
| 333 return; |
| 334 if (delegate_->AsConfirmInfoBarDelegate()->Cancel()) |
| 334 [self removeSelf]; | 335 [self removeSelf]; |
| 335 } | 336 } |
| 336 | 337 |
| 337 // Confirm infobars can have OK and/or cancel buttons, depending on | 338 // Confirm infobars can have OK and/or cancel buttons, depending on |
| 338 // the return value of GetButtons(). We create each button if | 339 // the return value of GetButtons(). We create each button if |
| 339 // required and position them to the left of the close button. | 340 // required and position them to the left of the close button. |
| 340 - (void)addAdditionalControls { | 341 - (void)addAdditionalControls { |
| 341 ConfirmInfoBarDelegate* delegate = delegate_->AsConfirmInfoBarDelegate(); | 342 ConfirmInfoBarDelegate* delegate = delegate_->AsConfirmInfoBarDelegate(); |
| 342 DCHECK(delegate); | 343 DCHECK(delegate); |
| 343 int visibleButtons = delegate->GetButtons(); | 344 int visibleButtons = delegate->GetButtons(); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 422 atOffset:[message length] | 423 atOffset:[message length] |
| 423 font:font | 424 font:font |
| 424 messageColor:[NSColor blackColor] | 425 messageColor:[NSColor blackColor] |
| 425 linkColor:[NSColor blueColor]]; | 426 linkColor:[NSColor blueColor]]; |
| 426 } | 427 } |
| 427 | 428 |
| 428 // Called when someone clicks on the link in the infobar. This method | 429 // Called when someone clicks on the link in the infobar. This method |
| 429 // is called by the InfobarTextField on its delegate (the | 430 // is called by the InfobarTextField on its delegate (the |
| 430 // LinkInfoBarController). | 431 // LinkInfoBarController). |
| 431 - (void)linkClicked { | 432 - (void)linkClicked { |
| 433 if (![self isOwned]) |
| 434 return; |
| 432 WindowOpenDisposition disposition = | 435 WindowOpenDisposition disposition = |
| 433 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 436 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
| 434 if (delegate_ && | 437 if (delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition)) |
| 435 delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition)) | |
| 436 [self removeSelf]; | 438 [self removeSelf]; |
| 437 } | 439 } |
| 438 | 440 |
| 439 @end | 441 @end |
| 440 | 442 |
| 441 | 443 |
| 442 ////////////////////////////////////////////////////////////////////////// | 444 ////////////////////////////////////////////////////////////////////////// |
| 443 // CreateInfoBar() implementations | 445 // CreateInfoBar() implementations |
| 444 | 446 |
| 445 InfoBar* LinkInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { | 447 InfoBar* LinkInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { |
| 446 LinkInfoBarController* controller = | 448 LinkInfoBarController* controller = |
| 447 [[LinkInfoBarController alloc] initWithDelegate:this owner:owner]; | 449 [[LinkInfoBarController alloc] initWithDelegate:this owner:owner]; |
| 448 return new InfoBar(controller, this); | 450 return new InfoBar(controller, this); |
| 449 } | 451 } |
| 450 | 452 |
| 451 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { | 453 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { |
| 452 ConfirmInfoBarController* controller = | 454 ConfirmInfoBarController* controller = |
| 453 [[ConfirmInfoBarController alloc] initWithDelegate:this owner:owner]; | 455 [[ConfirmInfoBarController alloc] initWithDelegate:this owner:owner]; |
| 454 return new InfoBar(controller, this); | 456 return new InfoBar(controller, this); |
| 455 } | 457 } |
| OLD | NEW |