Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / find_bar / find_bar_cocoa_controller.mm
blob72699bf026f7542d130fe868754923d9995ffd93
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
7 #include "base/auto_reset.h"
8 #include "base/mac/bundle_locations.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/browser_window.h"
12 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
13 #import "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h"
14 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
15 #import "chrome/browser/ui/cocoa/find_bar/find_bar_text_field.h"
16 #import "chrome/browser/ui/cocoa/find_bar/find_bar_text_field_cell.h"
17 #import "chrome/browser/ui/cocoa/image_button_cell.h"
18 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
19 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
20 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "grit/theme_resources.h"
25 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
26 #import "ui/base/cocoa/find_pasteboard.h"
27 #import "ui/base/cocoa/focus_tracker.h"
28 #import "ui/base/cocoa/nsview_additions.h"
29 #include "ui/base/l10n/l10n_util_mac.h"
30 #include "ui/resources/grit/ui_resources.h"
32 using content::NativeWebKeyboardEvent;
34 const float kFindBarOpenDuration = 0.2;
35 const float kFindBarCloseDuration = 0.15;
36 const float kFindBarMoveDuration = 0.15;
37 const float kRightEdgeOffset = 25;
40 @interface FindBarCocoaController (PrivateMethods) <NSAnimationDelegate>
41 // Returns the appropriate frame for a hidden find bar.
42 - (NSRect)hiddenFindBarFrame;
44 // Animates the given |view| to the given |endFrame| within |duration| seconds.
45 // Returns a new NSViewAnimation.
46 - (NSViewAnimation*)createAnimationForView:(NSView*)view
47                                    toFrame:(NSRect)endFrame
48                                   duration:(float)duration;
50 // Sets the frame of |findBarView_|.  |duration| is ignored if |animate| is NO.
51 - (void)setFindBarFrame:(NSRect)endFrame
52                 animate:(BOOL)animate
53                duration:(float)duration;
55 // Returns the horizontal position the FindBar should use in order to avoid
56 // overlapping with the current find result, if there's one.
57 - (float)findBarHorizontalPosition;
59 // Adjusts the horizontal position if necessary to avoid overlapping with the
60 // current find result.
61 - (void)moveFindBarIfNecessary:(BOOL)animate;
63 // Puts |text| into the find bar and enables the buttons, but doesn't start a
64 // new search for |text|.
65 - (void)prepopulateText:(NSString*)text;
67 // Clears the find results for all tabs in browser associated with this find
68 // bar. If |suppressPboardUpdateActions_| is true then the current tab is not
69 // cleared.
70 - (void)clearFindResultsForCurrentBrowser;
72 - (BrowserWindowController*)browserWindowController;
73 @end
75 @implementation FindBarCocoaController
77 @synthesize findBarView = findBarView_;
79 - (id)initWithBrowser:(Browser*)browser {
80   if ((self = [super initWithNibName:@"FindBar"
81                               bundle:base::mac::FrameworkBundle()])) {
82     [[NSNotificationCenter defaultCenter]
83         addObserver:self
84            selector:@selector(findPboardUpdated:)
85                name:kFindPasteboardChangedNotification
86              object:[FindPasteboard sharedInstance]];
87     browser_ = browser;
88   }
89   return self;
92 - (void)dealloc {
93   [self browserWillBeDestroyed];
94   [super dealloc];
97 - (void)browserWillBeDestroyed {
98   // All animations should have been explicitly stopped before a tab is closed.
99   DCHECK(!showHideAnimation_.get());
100   DCHECK(!moveAnimation_.get());
101   [[NSNotificationCenter defaultCenter] removeObserver:self];
102   browser_ = nullptr;
105 - (void)setFindBarBridge:(FindBarBridge*)findBarBridge {
106   DCHECK(!findBarBridge_);  // should only be called once.
107   findBarBridge_ = findBarBridge;
110 - (void)awakeFromNib {
111   [[closeButton_ cell] setImageID:IDR_CLOSE_1
112                    forButtonState:image_button_cell::kDefaultState];
113   [[closeButton_ cell] setImageID:IDR_CLOSE_1_H
114                    forButtonState:image_button_cell::kHoverState];
115   [[closeButton_ cell] setImageID:IDR_CLOSE_1_P
116                    forButtonState:image_button_cell::kPressedState];
117   [[closeButton_ cell] setImageID:IDR_CLOSE_1
118                    forButtonState:image_button_cell::kDisabledState];
120   [closeButton_ setToolTip:l10n_util::GetNSString(
121        IDS_FIND_IN_PAGE_CLOSE_TOOLTIP)];
122   [previousButton_ setToolTip:l10n_util::GetNSString(
123        IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP)];
124   [nextButton_ setToolTip:l10n_util::GetNSString(
125        IDS_FIND_IN_PAGE_NEXT_TOOLTIP)];
127   [findBarView_ setFrame:[self hiddenFindBarFrame]];
128   defaultWidth_ = NSWidth([findBarView_ frame]);
129   [[self view] setHidden:YES];
131   [self prepopulateText:[[FindPasteboard sharedInstance] findText]];
134 - (IBAction)close:(id)sender {
135   if (findBarBridge_)
136     findBarBridge_->GetFindBarController()->EndFindSession(
137         FindBarController::kKeepSelectionOnPage,
138         FindBarController::kKeepResultsInFindBox);
140   // Turn off hover state on close button else the button will remain
141   // hovered when we bring the find bar back up.
142   // crbug.com/227424
143   [[closeButton_ cell] setIsMouseInside:NO];
146 - (IBAction)previousResult:(id)sender {
147   if (findBarBridge_) {
148     FindTabHelper* findTabHelper = FindTabHelper::FromWebContents(
149         findBarBridge_->GetFindBarController()->web_contents());
150     findTabHelper->StartFinding(
151         base::SysNSStringToUTF16([findText_ stringValue]),
152         false, false);
153   }
156 - (IBAction)nextResult:(id)sender {
157   if (findBarBridge_) {
158     FindTabHelper* findTabHelper = FindTabHelper::FromWebContents(
159         findBarBridge_->GetFindBarController()->web_contents());
160     findTabHelper->StartFinding(
161         base::SysNSStringToUTF16([findText_ stringValue]),
162         true, false);
163   }
166 - (void)findPboardUpdated:(NSNotification*)notification {
167   if (!suppressPboardUpdateActions_)
168     [self prepopulateText:[[FindPasteboard sharedInstance] findText]];
169   [self clearFindResultsForCurrentBrowser];
172 - (void)positionFindBarViewAtMaxY:(CGFloat)maxY maxWidth:(CGFloat)maxWidth {
173   NSView* containerView = [self view];
174   CGFloat containerHeight = NSHeight([containerView frame]);
175   CGFloat containerWidth = std::min(maxWidth, defaultWidth_);
177   // Adjust where we'll actually place the find bar.
178   maxY += [containerView cr_lineWidth];
179   maxY_ = maxY;
180   CGFloat x = [self findBarHorizontalPosition];
181   NSRect newFrame = NSMakeRect(x, maxY - containerHeight,
182                                containerWidth, containerHeight);
184   if (moveAnimation_.get() != nil) {
185     NSRect frame = [containerView frame];
186     [moveAnimation_ stopAnimation];
187     // Restore to the X position before the animation was stopped. The Y
188     // position is immediately adjusted.
189     frame.origin.y = newFrame.origin.y;
190     [containerView setFrame:frame];
191     moveAnimation_.reset([self createAnimationForView:containerView
192                                               toFrame:newFrame
193                                              duration:kFindBarMoveDuration]);
194   } else {
195     [containerView setFrame:newFrame];
196   }
199 - (BOOL)isOffTheRecordProfile {
200   return browser_ && browser_->profile() &&
201          browser_->profile()->IsOffTheRecord();
204 // NSControl delegate method.
205 - (void)controlTextDidChange:(NSNotification*)aNotification {
206   if (!findBarBridge_)
207     return;
209   content::WebContents* webContents =
210       findBarBridge_->GetFindBarController()->web_contents();
211   if (!webContents)
212     return;
213   FindTabHelper* findTabHelper = FindTabHelper::FromWebContents(webContents);
215   NSString* findText = [findText_ stringValue];
216   if (![self isOffTheRecordProfile]) {
217     base::AutoReset<BOOL> suppressReset(&suppressPboardUpdateActions_, YES);
218     [[FindPasteboard sharedInstance] setFindText:findText];
219   }
221   if ([findText length] > 0) {
222     findTabHelper->
223         StartFinding(base::SysNSStringToUTF16(findText), true, false);
224   } else {
225     // The textbox is empty so we reset.
226     findTabHelper->StopFinding(FindBarController::kClearSelectionOnPage);
227     [self updateUIForFindResult:findTabHelper->find_result()
228                        withText:base::string16()];
229   }
232 // NSControl delegate method
233 - (BOOL)control:(NSControl*)control
234     textView:(NSTextView*)textView
235     doCommandBySelector:(SEL)command {
236   if (command == @selector(insertNewline:)) {
237     // Pressing Return
238     NSEvent* event = [NSApp currentEvent];
240     if ([event modifierFlags] & NSShiftKeyMask)
241       [previousButton_ performClick:nil];
242     else
243       [nextButton_ performClick:nil];
245     return YES;
246   } else if (command == @selector(insertLineBreak:)) {
247     // Pressing Ctrl-Return
248     if (findBarBridge_) {
249       findBarBridge_->GetFindBarController()->EndFindSession(
250           FindBarController::kActivateSelectionOnPage,
251           FindBarController::kClearResultsInFindBox);
252     }
253     return YES;
254   } else if (command == @selector(pageUp:) ||
255              command == @selector(pageUpAndModifySelection:) ||
256              command == @selector(scrollPageUp:) ||
257              command == @selector(pageDown:) ||
258              command == @selector(pageDownAndModifySelection:) ||
259              command == @selector(scrollPageDown:) ||
260              command == @selector(scrollToBeginningOfDocument:) ||
261              command == @selector(scrollToEndOfDocument:) ||
262              command == @selector(moveUp:) ||
263              command == @selector(moveDown:)) {
264     content::WebContents* web_contents =
265         findBarBridge_->GetFindBarController()->web_contents();
266     if (!web_contents)
267       return NO;
269     // Sanity-check to make sure we got a keyboard event.
270     NSEvent* event = [NSApp currentEvent];
271     if ([event type] != NSKeyDown && [event type] != NSKeyUp)
272       return NO;
274     // Forward the event to the renderer.
275     // TODO(rohitrao): Should this call -[BaseView keyEvent:]?  Is there code in
276     // that function that we want to keep or avoid? Calling
277     // |ForwardKeyboardEvent()| directly ignores edit commands, which breaks
278     // cmd-up/down if we ever decide to include |moveToBeginningOfDocument:| in
279     // the list above.
280     content::RenderViewHost* render_view_host =
281         web_contents->GetRenderViewHost();
282     render_view_host->ForwardKeyboardEvent(NativeWebKeyboardEvent(event));
283     return YES;
284   }
286   return NO;
289 // Methods from FindBar
290 - (void)showFindBar:(BOOL)animate {
291   // Save the currently-focused view.  |findBarView_| is in the view
292   // hierarchy by now.  showFindBar can be called even when the
293   // findbar is already open, so do not overwrite an already saved
294   // view.
295   if (!focusTracker_.get())
296     focusTracker_.reset(
297         [[FocusTracker alloc] initWithWindow:[findBarView_ window]]);
299   // The browser window might have changed while the FindBar was hidden.
300   // Update its position now.
301   [[self browserWindowController] layoutSubviews];
303   // Move to the correct horizontal position first, to prevent the FindBar
304   // from jumping around when switching tabs.
305   // Prevent jumping while the FindBar is animating (hiding, then showing) too.
306   if (![self isFindBarVisible])
307     [self moveFindBarIfNecessary:NO];
309   // Animate the view into place.
310   NSRect frame = [findBarView_ frame];
311   frame.origin = NSZeroPoint;
312   [self setFindBarFrame:frame animate:animate duration:kFindBarOpenDuration];
315 - (void)hideFindBar:(BOOL)animate {
316   NSRect frame = [self hiddenFindBarFrame];
317   [self setFindBarFrame:frame animate:animate duration:kFindBarCloseDuration];
320 - (void)stopAnimation {
321   if (showHideAnimation_.get()) {
322     [showHideAnimation_ stopAnimation];
323     showHideAnimation_.reset(nil);
324   }
325   if (moveAnimation_.get()) {
326     [moveAnimation_ stopAnimation];
327     moveAnimation_.reset(nil);
328   }
331 - (void)setFocusAndSelection {
332   [[findText_ window] makeFirstResponder:findText_];
334   // Enable the buttons if the find text is non-empty.
335   BOOL buttonsEnabled = ([[findText_ stringValue] length] > 0) ? YES : NO;
336   [previousButton_ setEnabled:buttonsEnabled];
337   [nextButton_ setEnabled:buttonsEnabled];
340 - (void)restoreSavedFocus {
341   if (!(focusTracker_.get() &&
342         [focusTracker_ restoreFocusInWindow:[findBarView_ window]])) {
343     // Fall back to giving focus to the tab contents.
344     findBarBridge_->GetFindBarController()->web_contents()->Focus();
345   }
346   focusTracker_.reset(nil);
349 - (void)setFindText:(NSString*)findText
350       selectedRange:(const NSRange&)selectedRange {
351   [findText_ setStringValue:findText];
353   if (![self isOffTheRecordProfile]) {
354     // Make sure the text in the find bar always ends up in the find pasteboard
355     // (and, via notifications, in the other find bars too).
356     base::AutoReset<BOOL> suppressReset(&suppressPboardUpdateActions_, YES);
357     [[FindPasteboard sharedInstance] setFindText:findText];
358   }
360   NSText* editor = [findText_ currentEditor];
361   if (selectedRange.location != NSNotFound)
362     [editor setSelectedRange:selectedRange];
365 - (NSString*)findText {
366   return [findText_ stringValue];
369 - (NSRange)selectedRange {
370   NSText* editor = [findText_ currentEditor];
371   return (editor != nil) ? [editor selectedRange] : NSMakeRange(NSNotFound, 0);
374 - (NSString*)matchCountText {
375   return [[findText_ findBarTextFieldCell] resultsString];
378 - (void)updateFindBarForChangedWebContents {
379   content::WebContents* contents =
380       findBarBridge_->GetFindBarController()->web_contents();
381   if (!contents)
382     return;
383   FindTabHelper* findTabHelper = FindTabHelper::FromWebContents(contents);
385   // If the find UI is visible but the results are cleared then also clear
386   // the results label and update the buttons.
387   if (findTabHelper->find_ui_active() &&
388       findTabHelper->previous_find_text().empty()) {
389     BOOL buttonsEnabled = [[findText_ stringValue] length] > 0 ? YES : NO;
390     [previousButton_ setEnabled:buttonsEnabled];
391     [nextButton_ setEnabled:buttonsEnabled];
392     [[findText_ findBarTextFieldCell] clearResults];
393   }
396 - (void)clearResults:(const FindNotificationDetails&)results {
397   // Just call updateUIForFindResult, which will take care of clearing
398   // the search text and the results label.
399   [self updateUIForFindResult:results withText:base::string16()];
402 - (void)updateUIForFindResult:(const FindNotificationDetails&)result
403                      withText:(const base::string16&)findText {
404   // If we don't have any results and something was passed in, then
405   // that means someone pressed Cmd-G while the Find box was
406   // closed. In that case we need to repopulate the Find box with what
407   // was passed in.
408   if ([[findText_ stringValue] length] == 0 && !findText.empty()) {
409     [findText_ setStringValue:base::SysUTF16ToNSString(findText)];
410     [findText_ selectText:self];
411   }
413   // Make sure Find Next and Find Previous are enabled if we found any matches.
414   BOOL buttonsEnabled = result.number_of_matches() > 0;
415   [previousButton_ setEnabled:buttonsEnabled];
416   [nextButton_ setEnabled:buttonsEnabled];
418   // Update the results label.
419   BOOL validRange = result.active_match_ordinal() != -1 &&
420                     result.number_of_matches() != -1;
421   NSString* searchString = [findText_ stringValue];
422   if ([searchString length] > 0 && validRange) {
423     [[findText_ findBarTextFieldCell]
424         setActiveMatch:result.active_match_ordinal()
425                     of:result.number_of_matches()];
426   } else {
427     // If there was no text entered, we don't show anything in the results area.
428     [[findText_ findBarTextFieldCell] clearResults];
429   }
431   [findText_ resetFieldEditorFrameIfNeeded];
433   // If we found any results, reset the focus tracker, so we always
434   // restore focus to the tab contents.
435   if (result.number_of_matches() > 0)
436     focusTracker_.reset(nil);
438   // Adjust the FindBar position, even when there are no matches (so that it
439   // goes back to the default position, if required).
440   [self moveFindBarIfNecessary:[self isFindBarVisible]];
443 - (BOOL)isFindBarVisible {
444   // Find bar is visible if any part of it is on the screen.
445   return NSIntersectsRect([[self view] bounds], [findBarView_ frame]);
448 - (BOOL)isFindBarAnimating {
449   return (showHideAnimation_.get() != nil) || (moveAnimation_.get() != nil);
452 // NSAnimation delegate methods.
453 - (void)animationDidEnd:(NSAnimation*)animation {
454   // Autorelease the animations (cannot use release because the animation object
455   // is still on the stack.
456   if (animation == showHideAnimation_.get()) {
457     [showHideAnimation_.release() autorelease];
458   } else if (animation == moveAnimation_.get()) {
459     [moveAnimation_.release() autorelease];
460   } else {
461     NOTREACHED();
462   }
464   // If the find bar is not visible, make it actually hidden, so it'll no longer
465   // respond to key events.
466   [[self view] setHidden:![self isFindBarVisible]];
469 - (gfx::Point)findBarWindowPosition {
470   gfx::Rect viewRect(NSRectToCGRect([[self view] frame]));
471   // Convert Cocoa coordinates (Y growing up) to Y growing down.
472   // Offset from |maxY_|, which represents the content view's top, instead
473   // of from the superview, which represents the whole browser window.
474   viewRect.set_y(maxY_ - viewRect.bottom());
475   return viewRect.origin();
478 - (int)findBarWidth {
479   return NSWidth([[self view] frame]);
482 @end
484 @implementation FindBarCocoaController (PrivateMethods)
486 - (NSRect)hiddenFindBarFrame {
487   NSRect frame = [findBarView_ frame];
488   NSRect containerBounds = [[self view] bounds];
489   frame.origin = NSMakePoint(NSMinX(containerBounds), NSMaxY(containerBounds));
490   return frame;
493 - (NSViewAnimation*)createAnimationForView:(NSView*)view
494                                    toFrame:(NSRect)endFrame
495                                   duration:(float)duration {
496   NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
497       view, NSViewAnimationTargetKey,
498       [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, nil];
500   NSViewAnimation* animation =
501       [[NSViewAnimation alloc]
502         initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
503   [animation gtm_setDuration:duration
504                    eventMask:NSLeftMouseUpMask];
505   [animation setDelegate:self];
506   [animation startAnimation];
507   return animation;
510 - (void)setFindBarFrame:(NSRect)endFrame
511                 animate:(BOOL)animate
512                duration:(float)duration {
513   // Save the current frame.
514   NSRect startFrame = [findBarView_ frame];
516   // Stop any existing animations.
517   [showHideAnimation_ stopAnimation];
519   if (!animate) {
520     [findBarView_ setFrame:endFrame];
521     [[self view] setHidden:![self isFindBarVisible]];
522     showHideAnimation_.reset(nil);
523     return;
524   }
526   // If animating, ensure that the find bar is not hidden. Hidden status will be
527   // updated at the end of the animation.
528   [[self view] setHidden:NO];
530   // Reset the frame to what was saved above.
531   [findBarView_ setFrame:startFrame];
533   showHideAnimation_.reset([self createAnimationForView:findBarView_
534                                                 toFrame:endFrame
535                                                duration:duration]);
538 - (float)findBarHorizontalPosition {
539   // Get the rect of the FindBar.
540   NSView* view = [self view];
541   NSRect frame = [view frame];
542   gfx::Rect viewRect(NSRectToCGRect(frame));
544   if (!findBarBridge_ || !findBarBridge_->GetFindBarController())
545     return frame.origin.x;
546   content::WebContents* contents =
547       findBarBridge_->GetFindBarController()->web_contents();
548   if (!contents)
549     return frame.origin.x;
551   // Get the size of the container.
552   gfx::Rect containerRect(contents->GetContainerBounds().size());
554   // Position the FindBar on the top right corner.
555   viewRect.set_x(
556       containerRect.width() - viewRect.width() - kRightEdgeOffset);
557   // Convert from Cocoa coordinates (Y growing up) to Y growing down.
558   // Notice that the view frame's Y offset is relative to the whole window,
559   // while GetLocationForFindbarView() expects it relative to the
560   // content's boundaries. |maxY_| has the correct placement in Cocoa coords,
561   // so we just have to invert the Y coordinate.
562   viewRect.set_y(maxY_ - viewRect.bottom());
564   // Get the rect of the current find result, if there is one.
565   const FindNotificationDetails& findResult =
566       FindTabHelper::FromWebContents(contents)->find_result();
567   if (findResult.number_of_matches() == 0)
568     return viewRect.x();
569   gfx::Rect selectionRect(findResult.selection_rect());
571   // Adjust |view_rect| to avoid the |selection_rect| within |container_rect|.
572   gfx::Rect newPos = FindBarController::GetLocationForFindbarView(
573       viewRect, containerRect, selectionRect);
575   return newPos.x();
578 - (void)moveFindBarIfNecessary:(BOOL)animate {
579   // Don't animate during tests.
580   if (FindBarBridge::disable_animations_during_testing_)
581     animate = NO;
583   NSView* view = [self view];
584   NSRect frame = [view frame];
585   float x = [self findBarHorizontalPosition];
586   if (frame.origin.x == x)
587     return;
589   if (animate) {
590     [moveAnimation_ stopAnimation];
591     // Restore to the position before the animation was stopped.
592     [view setFrame:frame];
593     frame.origin.x = x;
594     moveAnimation_.reset([self createAnimationForView:view
595                                               toFrame:frame
596                                              duration:kFindBarMoveDuration]);
597   } else {
598     frame.origin.x = x;
599     [view setFrame:frame];
600   }
603 - (void)prepopulateText:(NSString*)text {
604   [self setFindText:text selectedRange:NSMakeRange(NSNotFound, 0)];
606   // Has to happen after |ClearResults()| above.
607   BOOL buttonsEnabled = [text length] > 0 ? YES : NO;
608   [previousButton_ setEnabled:buttonsEnabled];
609   [nextButton_ setEnabled:buttonsEnabled];
612 - (void)clearFindResultsForCurrentBrowser {
613   if (!browser_)
614     return;
616   content::WebContents* activeWebContents =
617       findBarBridge_->GetFindBarController()->web_contents();
619   TabStripModel* tabStripModel = browser_->tab_strip_model();
620   for (int i = 0; i < tabStripModel->count(); ++i) {
621     content::WebContents* webContents = tabStripModel->GetWebContentsAt(i);
622     if (suppressPboardUpdateActions_ && activeWebContents == webContents)
623       continue;
624     FindTabHelper* findTabHelper =
625         FindTabHelper::FromWebContents(webContents);
626     findTabHelper->StopFinding(FindBarController::kClearSelectionOnPage);
627     findBarBridge_->ClearResults(findTabHelper->find_result());
628   }
631 - (BrowserWindowController*)browserWindowController {
632   if (!browser_)
633     return nil;
634   return [BrowserWindowController
635       browserWindowControllerForWindow:browser_->window()->GetNativeWindow()];
638 @end