Home | History | Annotate | Download | only in shell
      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.
      4 
      5 #include "content/shell/shell.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #import "base/mac/scoped_nsobject.h"
     11 #include "base/strings/string_piece.h"
     12 #include "base/strings/sys_string_conversions.h"
     13 #include "content/public/browser/native_web_keyboard_event.h"
     14 #include "content/public/browser/web_contents.h"
     15 #include "content/public/browser/web_contents_view.h"
     16 #include "content/shell/app/resource.h"
     17 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
     18 #include "url/gurl.h"
     19 
     20 #if !defined(MAC_OS_X_VERSION_10_7) || \
     21     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
     22 
     23 enum {
     24   NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
     25   NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
     26 };
     27 
     28 #endif // MAC_OS_X_VERSION_10_7
     29 
     30 // Receives notification that the window is closing so that it can start the
     31 // tear-down process. Is responsible for deleting itself when done.
     32 @interface ContentShellWindowDelegate : NSObject<NSWindowDelegate> {
     33  @private
     34   content::Shell* shell_;
     35 }
     36 - (id)initWithShell:(content::Shell*)shell;
     37 @end
     38 
     39 @implementation ContentShellWindowDelegate
     40 
     41 - (id)initWithShell:(content::Shell*)shell {
     42   if ((self = [super init])) {
     43     shell_ = shell;
     44   }
     45   return self;
     46 }
     47 
     48 // Called when the window is about to close. Perform the self-destruction
     49 // sequence by getting rid of the shell and removing it and the window from
     50 // the various global lists. By returning YES, we allow the window to be
     51 // removed from the screen.
     52 - (BOOL)windowShouldClose:(id)window {
     53   [window autorelease];
     54   delete shell_;
     55   [self release];
     56 
     57   return YES;
     58 }
     59 
     60 - (void)performAction:(id)sender {
     61   shell_->ActionPerformed([sender tag]);
     62 }
     63 
     64 - (void)takeURLStringValueFrom:(id)sender {
     65   shell_->URLEntered(base::SysNSStringToUTF8([sender stringValue]));
     66 }
     67 
     68 @end
     69 
     70 @interface CrShellWindow : UnderlayOpenGLHostingWindow {
     71  @private
     72   content::Shell* shell_;
     73 }
     74 - (void)setShell:(content::Shell*)shell;
     75 - (void)showDevTools:(id)sender;
     76 @end
     77 
     78 @implementation CrShellWindow
     79 
     80 - (void)setShell:(content::Shell*)shell {
     81   shell_ = shell;
     82 }
     83 
     84 - (void)showDevTools:(id)sender {
     85   shell_->ShowDevTools();
     86 }
     87 
     88 @end
     89 
     90 namespace {
     91 
     92 NSString* kWindowTitle = @"Content Shell";
     93 
     94 // Layout constants (in view coordinates)
     95 const CGFloat kButtonWidth = 72;
     96 const CGFloat kURLBarHeight = 24;
     97 
     98 // The minimum size of the window's content (in view coordinates)
     99 const CGFloat kMinimumWindowWidth = 400;
    100 const CGFloat kMinimumWindowHeight = 300;
    101 
    102 void MakeShellButton(NSRect* rect,
    103                      NSString* title,
    104                      NSView* parent,
    105                      int control,
    106                      NSView* target,
    107                      NSString* key,
    108                      NSUInteger modifier) {
    109   base::scoped_nsobject<NSButton> button(
    110       [[NSButton alloc] initWithFrame:*rect]);
    111   [button setTitle:title];
    112   [button setBezelStyle:NSSmallSquareBezelStyle];
    113   [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)];
    114   [button setTarget:target];
    115   [button setAction:@selector(performAction:)];
    116   [button setTag:control];
    117   [button setKeyEquivalent:key];
    118   [button setKeyEquivalentModifierMask:modifier];
    119   [parent addSubview:button];
    120   rect->origin.x += kButtonWidth;
    121 }
    122 
    123 }  // namespace
    124 
    125 namespace content {
    126 
    127 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
    128 }
    129 
    130 void Shell::PlatformCleanUp() {
    131 }
    132 
    133 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
    134   if (headless_)
    135     return;
    136 
    137   int id;
    138   switch (control) {
    139     case BACK_BUTTON:
    140       id = IDC_NAV_BACK;
    141       break;
    142     case FORWARD_BUTTON:
    143       id = IDC_NAV_FORWARD;
    144       break;
    145     case STOP_BUTTON:
    146       id = IDC_NAV_STOP;
    147       break;
    148     default:
    149       NOTREACHED() << "Unknown UI control";
    150       return;
    151   }
    152   [[[window_ contentView] viewWithTag:id] setEnabled:is_enabled];
    153 }
    154 
    155 void Shell::PlatformSetAddressBarURL(const GURL& url) {
    156   if (headless_)
    157     return;
    158 
    159   NSString* url_string = base::SysUTF8ToNSString(url.spec());
    160   [url_edit_view_ setStringValue:url_string];
    161 }
    162 
    163 void Shell::PlatformSetIsLoading(bool loading) {
    164 }
    165 
    166 void Shell::PlatformCreateWindow(int width, int height) {
    167   if (headless_) {
    168     content_width_ = width;
    169     content_height_ = height;
    170     return;
    171   }
    172 
    173   NSRect initial_window_bounds =
    174       NSMakeRect(0, 0, width, height + kURLBarHeight);
    175   NSRect content_rect = initial_window_bounds;
    176   NSUInteger style_mask = NSTitledWindowMask |
    177                           NSClosableWindowMask |
    178                           NSMiniaturizableWindowMask |
    179                           NSResizableWindowMask;
    180   CrShellWindow* window =
    181       [[CrShellWindow alloc] initWithContentRect:content_rect
    182                                        styleMask:style_mask
    183                                          backing:NSBackingStoreBuffered
    184                                            defer:NO];
    185   window_ = window;
    186   [window setShell:this];
    187   [window_ setTitle:kWindowTitle];
    188   NSView* content = [window_ contentView];
    189 
    190   // If the window is allowed to get too small, it will wreck the view bindings.
    191   NSSize min_size = NSMakeSize(kMinimumWindowWidth, kMinimumWindowHeight);
    192   min_size = [content convertSize:min_size toView:nil];
    193   // Note that this takes window coordinates.
    194   [window_ setContentMinSize:min_size];
    195 
    196   // Set the shell window to participate in Lion Fullscreen mode. Set
    197   // Setting this flag has no effect on Snow Leopard or earlier.
    198   NSUInteger collectionBehavior = [window_ collectionBehavior];
    199   collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
    200   [window_ setCollectionBehavior:collectionBehavior];
    201 
    202   // Rely on the window delegate to clean us up rather than immediately
    203   // releasing when the window gets closed. We use the delegate to do
    204   // everything from the autorelease pool so the shell isn't on the stack
    205   // during cleanup (ie, a window close from javascript).
    206   [window_ setReleasedWhenClosed:NO];
    207 
    208   // Create a window delegate to watch for when it's asked to go away. It will
    209   // clean itself up so we don't need to hold a reference.
    210   ContentShellWindowDelegate* delegate =
    211       [[ContentShellWindowDelegate alloc] initWithShell:this];
    212   [window_ setDelegate:delegate];
    213 
    214   NSRect button_frame =
    215       NSMakeRect(0, NSMaxY(initial_window_bounds) - kURLBarHeight,
    216                  kButtonWidth, kURLBarHeight);
    217 
    218   MakeShellButton(&button_frame, @"Back", content, IDC_NAV_BACK,
    219                   (NSView*)delegate, @"[", NSCommandKeyMask);
    220   MakeShellButton(&button_frame, @"Forward", content, IDC_NAV_FORWARD,
    221                   (NSView*)delegate, @"]", NSCommandKeyMask);
    222   MakeShellButton(&button_frame, @"Reload", content, IDC_NAV_RELOAD,
    223                   (NSView*)delegate, @"r", NSCommandKeyMask);
    224   MakeShellButton(&button_frame, @"Stop", content, IDC_NAV_STOP,
    225                   (NSView*)delegate, @".", NSCommandKeyMask);
    226 
    227   button_frame.size.width =
    228       NSWidth(initial_window_bounds) - NSMinX(button_frame);
    229   base::scoped_nsobject<NSTextField> url_edit_view(
    230       [[NSTextField alloc] initWithFrame:button_frame]);
    231   [content addSubview:url_edit_view];
    232   [url_edit_view setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
    233   [url_edit_view setTarget:delegate];
    234   [url_edit_view setAction:@selector(takeURLStringValueFrom:)];
    235   [[url_edit_view cell] setWraps:NO];
    236   [[url_edit_view cell] setScrollable:YES];
    237   url_edit_view_ = url_edit_view.get();
    238 
    239   // show the window
    240   [window_ makeKeyAndOrderFront:nil];
    241 }
    242 
    243 void Shell::PlatformSetContents() {
    244   NSView* web_view = web_contents_->GetView()->GetNativeView();
    245   [web_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
    246 
    247   if (headless_) {
    248     SizeTo(content_width_, content_height_);
    249     return;
    250   }
    251 
    252   NSView* content = [window_ contentView];
    253   [content addSubview:web_view];
    254 
    255   NSRect frame = [content bounds];
    256   frame.size.height -= kURLBarHeight;
    257   [web_view setFrame:frame];
    258   [web_view setNeedsDisplay:YES];
    259 }
    260 
    261 void Shell::SizeTo(int width, int height) {
    262   if (!headless_) {
    263     NOTREACHED();
    264     return;
    265   }
    266   NSView* web_view = web_contents_->GetView()->GetNativeView();
    267   NSRect frame = NSMakeRect(0, 0, width, height);
    268   [web_view setFrame:frame];
    269 }
    270 
    271 void Shell::PlatformResizeSubViews() {
    272   // Not needed; subviews are bound.
    273 }
    274 
    275 void Shell::PlatformSetTitle(const string16& title) {
    276   if (headless_)
    277     return;
    278 
    279   NSString* title_string = base::SysUTF16ToNSString(title);
    280   [window_ setTitle:title_string];
    281 }
    282 
    283 void Shell::Close() {
    284   if (headless_)
    285     delete this;
    286   else
    287     [window_ performClose:nil];
    288 }
    289 
    290 void Shell::ActionPerformed(int control) {
    291   switch (control) {
    292     case IDC_NAV_BACK:
    293       GoBackOrForward(-1);
    294       break;
    295     case IDC_NAV_FORWARD:
    296       GoBackOrForward(1);
    297       break;
    298     case IDC_NAV_RELOAD:
    299       Reload();
    300       break;
    301     case IDC_NAV_STOP:
    302       Stop();
    303       break;
    304   }
    305 }
    306 
    307 void Shell::URLEntered(std::string url_string) {
    308   if (!url_string.empty()) {
    309     GURL url(url_string);
    310     if (!url.has_scheme())
    311       url = GURL("http://" + url_string);
    312     LoadURL(url);
    313   }
    314 }
    315 
    316 void Shell::HandleKeyboardEvent(WebContents* source,
    317                                 const NativeWebKeyboardEvent& event) {
    318   if (event.skip_in_browser)
    319     return;
    320 
    321   // The event handling to get this strictly right is a tangle; cheat here a bit
    322   // by just letting the menus have a chance at it.
    323   if ([event.os_event type] == NSKeyDown) {
    324     if (([event.os_event modifierFlags] & NSCommandKeyMask) &&
    325         [[event.os_event characters] isEqual:@"l"]) {
    326       [window_ makeFirstResponder:url_edit_view_];
    327       return;
    328     }
    329 
    330     [[NSApp mainMenu] performKeyEquivalent:event.os_event];
    331   }
    332 }
    333 
    334 }  // namespace content
    335