• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3// found in the LICENSE file.
4
5#import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
6
7#include "base/command_line.h"
8#import "base/memory/scoped_nsobject.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/prefs/pref_service.h"
11#include "chrome/browser/prefs/scoped_user_pref_update.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/ui/browser_list.h"
14#import "chrome/browser/ui/cocoa/fast_resize_view.h"
15#import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
16#import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
17#import "chrome/browser/ui/cocoa/framed_browser_window.h"
18#import "chrome/browser/ui/cocoa/fullscreen_controller.h"
19#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
20#import "chrome/browser/ui/cocoa/tab_contents/previewable_contents_controller.h"
21#import "chrome/browser/ui/cocoa/tabs/side_tab_strip_controller.h"
22#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
23#import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
24#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/pref_names.h"
27#include "content/browser/renderer_host/render_widget_host_view.h"
28#include "content/browser/tab_contents/tab_contents.h"
29#include "content/browser/tab_contents/tab_contents_view.h"
30
31// Provide the forward-declarations of new 10.7 SDK symbols so they can be
32// called when building with the 10.5 SDK.
33#if !defined(MAC_OS_X_VERSION_10_7) || \
34    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
35
36@interface NSWindow (LionSDKDeclarations)
37- (void)toggleFullScreen:(id)sender;
38@end
39
40enum {
41  NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
42  NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
43};
44
45enum {
46  NSWindowFullScreenButton = 7
47};
48
49#endif  // MAC_OS_X_VERSION_10_7
50
51namespace {
52
53// Space between the incognito badge and the right edge of the window.
54const CGFloat kIncognitoBadgeOffset = 4;
55
56// Insets for the location bar, used when the full toolbar is hidden.
57// TODO(viettrungluu): We can argue about the "correct" insetting; I like the
58// following best, though arguably 0 inset is better/more correct.
59const CGFloat kLocBarLeftRightInset = 1;
60const CGFloat kLocBarTopInset = 0;
61const CGFloat kLocBarBottomInset = 1;
62
63}  // namespace
64
65@implementation BrowserWindowController(Private)
66
67// Create the appropriate tab strip controller based on whether or not side
68// tabs are enabled.
69- (void)createTabStripController {
70  Class factory = [TabStripController class];
71  if ([self useVerticalTabs])
72    factory = [SideTabStripController class];
73
74  DCHECK([previewableContentsController_ activeContainer]);
75  DCHECK([[previewableContentsController_ activeContainer] window]);
76  tabStripController_.reset([[factory alloc]
77      initWithView:[self tabStripView]
78        switchView:[previewableContentsController_ activeContainer]
79           browser:browser_.get()
80          delegate:self]);
81}
82
83- (void)saveWindowPositionIfNeeded {
84  if (browser_ != BrowserList::GetLastActive())
85    return;
86
87  if (!browser_->profile()->GetPrefs() ||
88      !browser_->ShouldSaveWindowPlacement()) {
89    return;
90  }
91
92  [self saveWindowPositionToPrefs:browser_->profile()->GetPrefs()];
93}
94
95- (void)saveWindowPositionToPrefs:(PrefService*)prefs {
96  // If we're in fullscreen mode, save the position of the regular window
97  // instead.
98  NSWindow* window = [self isFullscreen] ? savedRegularWindow_ : [self window];
99
100  // Window positions are stored relative to the origin of the primary monitor.
101  NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame];
102  NSScreen* windowScreen = [window screen];
103
104  // |windowScreen| can be nil (for example, if the monitor arrangement was
105  // changed while in fullscreen mode).  If we see a nil screen, return without
106  // saving.
107  // TODO(rohitrao): We should just not save anything for fullscreen windows.
108  // http://crbug.com/36479.
109  if (!windowScreen)
110    return;
111
112  // Start with the window's frame, which is in virtual coordinates.
113  // Do some y twiddling to flip the coordinate system.
114  gfx::Rect bounds(NSRectToCGRect([window frame]));
115  bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height());
116
117  // We also need to save the current work area, in flipped coordinates.
118  gfx::Rect workArea(NSRectToCGRect([windowScreen visibleFrame]));
119  workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height());
120
121  // Browser::SaveWindowPlacement is used for session restore.
122  if (browser_->ShouldSaveWindowPlacement())
123    browser_->SaveWindowPlacement(bounds, /*maximized=*/ false);
124
125  DictionaryPrefUpdate update(prefs, browser_->GetWindowPlacementKey().c_str());
126  DictionaryValue* windowPreferences = update.Get();
127  windowPreferences->SetInteger("left", bounds.x());
128  windowPreferences->SetInteger("top", bounds.y());
129  windowPreferences->SetInteger("right", bounds.right());
130  windowPreferences->SetInteger("bottom", bounds.bottom());
131  windowPreferences->SetBoolean("maximized", false);
132  windowPreferences->SetBoolean("always_on_top", false);
133  windowPreferences->SetInteger("work_area_left", workArea.x());
134  windowPreferences->SetInteger("work_area_top", workArea.y());
135  windowPreferences->SetInteger("work_area_right", workArea.right());
136  windowPreferences->SetInteger("work_area_bottom", workArea.bottom());
137}
138
139- (NSRect)window:(NSWindow*)window
140willPositionSheet:(NSWindow*)sheet
141       usingRect:(NSRect)defaultSheetRect {
142  // Position the sheet as follows:
143  //  - If the bookmark bar is hidden or shown as a bubble (on the NTP when the
144  //    bookmark bar is disabled), position the sheet immediately below the
145  //    normal toolbar.
146  //  - If the bookmark bar is shown (attached to the normal toolbar), position
147  //    the sheet below the bookmark bar.
148  //  - If the bookmark bar is currently animating, position the sheet according
149  //    to where the bar will be when the animation ends.
150  switch ([bookmarkBarController_ visualState]) {
151    case bookmarks::kShowingState: {
152      NSRect bookmarkBarFrame = [[bookmarkBarController_ view] frame];
153      defaultSheetRect.origin.y = bookmarkBarFrame.origin.y;
154      break;
155    }
156    case bookmarks::kHiddenState:
157    case bookmarks::kDetachedState: {
158      NSRect toolbarFrame = [[toolbarController_ view] frame];
159      defaultSheetRect.origin.y = toolbarFrame.origin.y;
160      break;
161    }
162    case bookmarks::kInvalidState:
163    default:
164      NOTREACHED();
165  }
166  return defaultSheetRect;
167}
168
169- (void)layoutSubviews {
170  // With the exception of the top tab strip, the subviews which we lay out are
171  // subviews of the content view, so we mainly work in the content view's
172  // coordinate system. Note, however, that the content view's coordinate system
173  // and the window's base coordinate system should coincide.
174  NSWindow* window = [self window];
175  NSView* contentView = [window contentView];
176  NSRect contentBounds = [contentView bounds];
177  CGFloat minX = NSMinX(contentBounds);
178  CGFloat minY = NSMinY(contentBounds);
179  CGFloat width = NSWidth(contentBounds);
180
181  // Suppress title drawing if necessary.
182  if ([window respondsToSelector:@selector(setShouldHideTitle:)])
183    [(id)window setShouldHideTitle:![self hasTitleBar]];
184
185  BOOL isFullscreen = [self isFullscreen];
186  CGFloat floatingBarHeight = [self floatingBarHeight];
187  // In fullscreen mode, |yOffset| accounts for the sliding position of the
188  // floating bar and the extra offset needed to dodge the menu bar.
189  CGFloat yOffset = isFullscreen ?
190      (floor((1 - floatingBarShownFraction_) * floatingBarHeight) -
191          [fullscreenController_ floatingBarVerticalOffset]) : 0;
192  CGFloat maxY = NSMaxY(contentBounds) + yOffset;
193  CGFloat startMaxY = maxY;
194
195  if ([self hasTabStrip] && ![self useVerticalTabs]) {
196    // If we need to lay out the top tab strip, replace |maxY| and |startMaxY|
197    // with higher values, and then lay out the tab strip.
198    NSRect windowFrame = [contentView convertRect:[window frame] fromView:nil];
199    startMaxY = maxY = NSHeight(windowFrame) + yOffset;
200    maxY = [self layoutTabStripAtMaxY:maxY width:width fullscreen:isFullscreen];
201  }
202
203  // Sanity-check |maxY|.
204  DCHECK_GE(maxY, minY);
205  DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset);
206
207  // The base class already positions the side tab strip on the left side
208  // of the window's content area and sizes it to take the entire vertical
209  // height. All that's needed here is to push everything over to the right,
210  // if necessary.
211  if ([self useVerticalTabs]) {
212    const CGFloat sideTabWidth = [[self tabStripView] bounds].size.width;
213    minX += sideTabWidth;
214    width -= sideTabWidth;
215  }
216
217  // Place the toolbar at the top of the reserved area.
218  maxY = [self layoutToolbarAtMinX:minX maxY:maxY width:width];
219
220  // If we're not displaying the bookmark bar below the infobar, then it goes
221  // immediately below the toolbar.
222  BOOL placeBookmarkBarBelowInfoBar = [self placeBookmarkBarBelowInfoBar];
223  if (!placeBookmarkBarBelowInfoBar)
224    maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
225
226  // The floating bar backing view doesn't actually add any height.
227  NSRect floatingBarBackingRect =
228      NSMakeRect(minX, maxY, width, floatingBarHeight);
229  [self layoutFloatingBarBackingView:floatingBarBackingRect
230                          fullscreen:isFullscreen];
231
232  // Place the find bar immediately below the toolbar/attached bookmark bar. In
233  // fullscreen mode, it hangs off the top of the screen when the bar is hidden.
234  // The find bar is unaffected by the side tab positioning.
235  [findBarCocoaController_ positionFindBarViewAtMaxY:maxY maxWidth:width];
236
237  // If in fullscreen mode, reset |maxY| to top of screen, so that the floating
238  // bar slides over the things which appear to be in the content area.
239  if (isFullscreen)
240    maxY = NSMaxY(contentBounds);
241
242  // Also place the infobar container immediate below the toolbar, except in
243  // fullscreen mode in which case it's at the top of the visual content area.
244  maxY = [self layoutInfoBarAtMinX:minX maxY:maxY width:width];
245
246  // If the bookmark bar is detached, place it next in the visual content area.
247  if (placeBookmarkBarBelowInfoBar)
248    maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
249
250  // Place the download shelf, if any, at the bottom of the view.
251  minY = [self layoutDownloadShelfAtMinX:minX minY:minY width:width];
252
253  // Finally, the content area takes up all of the remaining space.
254  NSRect contentAreaRect = NSMakeRect(minX, minY, width, maxY - minY);
255  [self layoutTabContentArea:contentAreaRect];
256
257  // Normally, we don't need to tell the toolbar whether or not to show the
258  // divider, but things break down during animation.
259  [toolbarController_
260      setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]];
261}
262
263- (CGFloat)floatingBarHeight {
264  if (![self isFullscreen])
265    return 0;
266
267  CGFloat totalHeight = [fullscreenController_ floatingBarVerticalOffset];
268
269  if ([self hasTabStrip])
270    totalHeight += NSHeight([[self tabStripView] frame]);
271
272  if ([self hasToolbar]) {
273    totalHeight += NSHeight([[toolbarController_ view] frame]);
274  } else if ([self hasLocationBar]) {
275    totalHeight += NSHeight([[toolbarController_ view] frame]) +
276                   kLocBarTopInset + kLocBarBottomInset;
277  }
278
279  if (![self placeBookmarkBarBelowInfoBar])
280    totalHeight += NSHeight([[bookmarkBarController_ view] frame]);
281
282  return totalHeight;
283}
284
285- (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY
286                          width:(CGFloat)width
287                     fullscreen:(BOOL)fullscreen {
288  // Nothing to do if no tab strip.
289  if (![self hasTabStrip])
290    return maxY;
291
292  NSView* tabStripView = [self tabStripView];
293  CGFloat tabStripHeight = NSHeight([tabStripView frame]);
294  maxY -= tabStripHeight;
295  [tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)];
296
297  // Set indentation.
298  [tabStripController_ setIndentForControls:(fullscreen ? 0 :
299      [[tabStripController_ class] defaultIndentForControls])];
300
301  // TODO(viettrungluu): Seems kind of bad -- shouldn't |-layoutSubviews| do
302  // this? Moreover, |-layoutTabs| will try to animate....
303  [tabStripController_ layoutTabs];
304
305  // Now lay out incognito badge together with the tab strip.
306  if (incognitoBadge_.get()) {
307    // Avoid the full-screen button.
308    CGFloat extraPadding = 0;
309    if ([[self window] respondsToSelector:@selector(toggleFullScreen:)]) {
310      NSButton* fullscreenButton =
311          [[self window] standardWindowButton:NSWindowFullScreenButton];
312      if (fullscreenButton)
313        extraPadding += [fullscreenButton frame].size.width;
314    }
315
316    // Actually place the badge *above* |maxY|, by +2 to miss the divider.
317    NSPoint origin = NSMakePoint(width - NSWidth([incognitoBadge_ frame]) -
318                                     kIncognitoBadgeOffset - extraPadding,
319                                 maxY + 2);
320    [incognitoBadge_ setFrameOrigin:origin];
321    [incognitoBadge_ setHidden:NO];  // Make sure it's shown.
322  }
323
324  return maxY;
325}
326
327- (CGFloat)layoutToolbarAtMinX:(CGFloat)minX
328                          maxY:(CGFloat)maxY
329                         width:(CGFloat)width {
330  NSView* toolbarView = [toolbarController_ view];
331  NSRect toolbarFrame = [toolbarView frame];
332  if ([self hasToolbar]) {
333    // The toolbar is present in the window, so we make room for it.
334    DCHECK(![toolbarView isHidden]);
335    toolbarFrame.origin.x = minX;
336    toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame);
337    toolbarFrame.size.width = width;
338    maxY -= NSHeight(toolbarFrame);
339  } else {
340    if ([self hasLocationBar]) {
341      // Location bar is present with no toolbar. Put a border of
342      // |kLocBar...Inset| pixels around the location bar.
343      // TODO(viettrungluu): This is moderately ridiculous. The toolbar should
344      // really be aware of what its height should be (the way the toolbar
345      // compression stuff is currently set up messes things up).
346      DCHECK(![toolbarView isHidden]);
347      toolbarFrame.origin.x = kLocBarLeftRightInset;
348      toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame) - kLocBarTopInset;
349      toolbarFrame.size.width = width - 2 * kLocBarLeftRightInset;
350      maxY -= kLocBarTopInset + NSHeight(toolbarFrame) + kLocBarBottomInset;
351    } else {
352      DCHECK([toolbarView isHidden]);
353    }
354  }
355  [toolbarView setFrame:toolbarFrame];
356  return maxY;
357}
358
359- (BOOL)placeBookmarkBarBelowInfoBar {
360  // If we are currently displaying the NTP detached bookmark bar or animating
361  // to/from it (from/to anything else), we display the bookmark bar below the
362  // infobar.
363  return [bookmarkBarController_ isInState:bookmarks::kDetachedState] ||
364      [bookmarkBarController_ isAnimatingToState:bookmarks::kDetachedState] ||
365      [bookmarkBarController_ isAnimatingFromState:bookmarks::kDetachedState];
366}
367
368- (CGFloat)layoutBookmarkBarAtMinX:(CGFloat)minX
369                              maxY:(CGFloat)maxY
370                             width:(CGFloat)width {
371  NSView* bookmarkBarView = [bookmarkBarController_ view];
372  NSRect bookmarkBarFrame = [bookmarkBarView frame];
373  BOOL oldHidden = [bookmarkBarView isHidden];
374  BOOL newHidden = ![self isBookmarkBarVisible];
375  if (oldHidden != newHidden)
376    [bookmarkBarView setHidden:newHidden];
377  bookmarkBarFrame.origin.x = minX;
378  bookmarkBarFrame.origin.y = maxY - NSHeight(bookmarkBarFrame);
379  bookmarkBarFrame.size.width = width;
380  [bookmarkBarView setFrame:bookmarkBarFrame];
381  maxY -= NSHeight(bookmarkBarFrame);
382
383  // TODO(viettrungluu): Does this really belong here? Calling it shouldn't be
384  // necessary in the non-NTP case.
385  [bookmarkBarController_ layoutSubviews];
386
387  return maxY;
388}
389
390- (void)layoutFloatingBarBackingView:(NSRect)frame
391                          fullscreen:(BOOL)fullscreen {
392  // Only display when in fullscreen mode.
393  if (fullscreen) {
394    // For certain window types such as app windows (e.g., the dev tools
395    // window), there's no actual overlay. (Displaying one would result in an
396    // overly sliding in only under the menu, which gives an ugly effect.)
397    if (floatingBarBackingView_.get()) {
398      BOOL aboveBookmarkBar = [self placeBookmarkBarBelowInfoBar];
399
400      // Insert it into the view hierarchy if necessary.
401      if (![floatingBarBackingView_ superview] ||
402          aboveBookmarkBar != floatingBarAboveBookmarkBar_) {
403        NSView* contentView = [[self window] contentView];
404        // z-order gets messed up unless we explicitly remove the floatingbar
405        // view and re-add it.
406        [floatingBarBackingView_ removeFromSuperview];
407        [contentView addSubview:floatingBarBackingView_
408                     positioned:(aboveBookmarkBar ?
409                                     NSWindowAbove : NSWindowBelow)
410                     relativeTo:[bookmarkBarController_ view]];
411        floatingBarAboveBookmarkBar_ = aboveBookmarkBar;
412      }
413
414      // Set its frame.
415      [floatingBarBackingView_ setFrame:frame];
416    }
417
418    // But we want the logic to work as usual (for show/hide/etc. purposes).
419    [fullscreenController_ overlayFrameChanged:frame];
420  } else {
421    // Okay to call even if |floatingBarBackingView_| is nil.
422    if ([floatingBarBackingView_ superview])
423      [floatingBarBackingView_ removeFromSuperview];
424  }
425}
426
427- (CGFloat)layoutInfoBarAtMinX:(CGFloat)minX
428                          maxY:(CGFloat)maxY
429                         width:(CGFloat)width {
430  NSView* containerView = [infoBarContainerController_ view];
431  NSRect containerFrame = [containerView frame];
432  maxY -= NSHeight(containerFrame);
433  maxY += [infoBarContainerController_ antiSpoofHeight];
434  containerFrame.origin.x = minX;
435  containerFrame.origin.y = maxY;
436  containerFrame.size.width = width;
437  [containerView setFrame:containerFrame];
438  return maxY;
439}
440
441- (CGFloat)layoutDownloadShelfAtMinX:(CGFloat)minX
442                                minY:(CGFloat)minY
443                               width:(CGFloat)width {
444  if (downloadShelfController_.get()) {
445    NSView* downloadView = [downloadShelfController_ view];
446    NSRect downloadFrame = [downloadView frame];
447    downloadFrame.origin.x = minX;
448    downloadFrame.origin.y = minY;
449    downloadFrame.size.width = width;
450    [downloadView setFrame:downloadFrame];
451    minY += NSHeight(downloadFrame);
452  }
453  return minY;
454}
455
456- (void)layoutTabContentArea:(NSRect)newFrame {
457  NSView* tabContentView = [self tabContentArea];
458  NSRect tabContentFrame = [tabContentView frame];
459
460  bool contentShifted =
461      NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
462      NSMinX(tabContentFrame) != NSMinX(newFrame);
463
464  tabContentFrame = newFrame;
465  [tabContentView setFrame:tabContentFrame];
466
467  // If the relayout shifts the content area up or down, let the renderer know.
468  if (contentShifted) {
469    if (TabContents* contents = browser_->GetSelectedTabContents()) {
470      if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
471        rwhv->WindowFrameChanged();
472    }
473  }
474}
475
476- (BOOL)shouldShowBookmarkBar {
477  DCHECK(browser_.get());
478  return browser_->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) ?
479      YES : NO;
480}
481
482- (BOOL)shouldShowDetachedBookmarkBar {
483  // NTP4 never detaches the bookmark bar.
484  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4))
485    return NO;
486
487  DCHECK(browser_.get());
488  TabContents* contents = browser_->GetSelectedTabContents();
489  return (contents &&
490          contents->ShouldShowBookmarkBar() &&
491          ![previewableContentsController_ isShowingPreview]);
492}
493
494- (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression {
495  CGFloat newHeight =
496      [toolbarController_ desiredHeightForCompression:compression];
497  NSRect toolbarFrame = [[toolbarController_ view] frame];
498  CGFloat deltaH = newHeight - toolbarFrame.size.height;
499
500  if (deltaH == 0)
501    return;
502
503  toolbarFrame.size.height = newHeight;
504  NSRect bookmarkFrame = [[bookmarkBarController_ view] frame];
505  bookmarkFrame.size.height = bookmarkFrame.size.height - deltaH;
506  [[toolbarController_ view] setFrame:toolbarFrame];
507  [[bookmarkBarController_ view] setFrame:bookmarkFrame];
508  [self layoutSubviews];
509}
510
511// TODO(rohitrao): This function has shrunk into uselessness, and
512// |-setFullscreen:| has grown rather large.  Find a good way to break up
513// |-setFullscreen:| into smaller pieces.  http://crbug.com/36449
514- (void)adjustUIForFullscreen:(BOOL)fullscreen {
515  // Create the floating bar backing view if necessary.
516  if (fullscreen && !floatingBarBackingView_.get() &&
517      ([self hasTabStrip] || [self hasToolbar] || [self hasLocationBar])) {
518    floatingBarBackingView_.reset(
519        [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]);
520  }
521}
522
523- (void)enableBarVisibilityUpdates {
524  // Early escape if there's nothing to do.
525  if (barVisibilityUpdatesEnabled_)
526    return;
527
528  barVisibilityUpdatesEnabled_ = YES;
529
530  if ([barVisibilityLocks_ count])
531    [fullscreenController_ ensureOverlayShownWithAnimation:NO delay:NO];
532  else
533    [fullscreenController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
534}
535
536- (void)disableBarVisibilityUpdates {
537  // Early escape if there's nothing to do.
538  if (!barVisibilityUpdatesEnabled_)
539    return;
540
541  barVisibilityUpdatesEnabled_ = NO;
542  [fullscreenController_ cancelAnimationAndTimers];
543}
544
545- (void)setUpOSFullScreenButton {
546  NSWindow* window = [self window];
547  if ([window respondsToSelector:@selector(toggleFullScreen:)]) {
548    NSWindowCollectionBehavior behavior = [window collectionBehavior];
549    behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
550    [window setCollectionBehavior:behavior];
551
552    NSButton* fullscreenButton =
553        [window standardWindowButton:NSWindowFullScreenButton];
554    [fullscreenButton setAction:@selector(enterFullscreen:)];
555    [fullscreenButton setTarget:self];
556  }
557}
558
559@end  // @implementation BrowserWindowController(Private)
560