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)owned { |
| 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 owned]) |
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 owned]) |
| 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 owned]) |
| 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 owned]) |
| 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 owned]) |
| 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 |