1 // Copyright 2013 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 #include "content/shell/browser/shell.h"
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/shell/app/resource.h"
16 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
19 #if !defined(MAC_OS_X_VERSION_10_7) || \
20 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
23 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
24 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
27 #endif // MAC_OS_X_VERSION_10_7
29 // Receives notification that the window is closing so that it can start the
30 // tear-down process. Is responsible for deleting itself when done.
31 @interface ContentShellWindowDelegate : NSObject<NSWindowDelegate> {
33 content::Shell* shell_;
35 - (id)initWithShell:(content::Shell*)shell;
38 @implementation ContentShellWindowDelegate
40 - (id)initWithShell:(content::Shell*)shell {
41 if ((self = [super init])) {
47 // Called when the window is about to close. Perform the self-destruction
48 // sequence by getting rid of the shell and removing it and the window from
49 // the various global lists. By returning YES, we allow the window to be
50 // removed from the screen.
51 - (BOOL)windowShouldClose:(id)window {
59 - (void)performAction:(id)sender {
60 shell_->ActionPerformed([sender tag]);
63 - (void)takeURLStringValueFrom:(id)sender {
64 shell_->URLEntered(base::SysNSStringToUTF8([sender stringValue]));
69 @interface CrShellWindow : UnderlayOpenGLHostingWindow {
71 content::Shell* shell_;
73 - (void)setShell:(content::Shell*)shell;
74 - (void)showDevTools:(id)sender;
77 @implementation CrShellWindow
79 - (void)setShell:(content::Shell*)shell {
83 - (void)showDevTools:(id)sender {
84 shell_->ShowDevTools();
91 NSString* kWindowTitle = @"Content Shell";
93 // Layout constants (in view coordinates)
94 const CGFloat kButtonWidth = 72;
95 const CGFloat kURLBarHeight = 24;
97 // The minimum size of the window's content (in view coordinates)
98 const CGFloat kMinimumWindowWidth = 400;
99 const CGFloat kMinimumWindowHeight = 300;
101 void MakeShellButton(NSRect* rect,
107 NSUInteger modifier) {
108 base::scoped_nsobject<NSButton> button(
109 [[NSButton alloc] initWithFrame:*rect]);
110 [button setTitle:title];
111 [button setBezelStyle:NSSmallSquareBezelStyle];
112 [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)];
113 [button setTarget:target];
114 [button setAction:@selector(performAction:)];
115 [button setTag:control];
116 [button setKeyEquivalent:key];
117 [button setKeyEquivalentModifierMask:modifier];
118 [parent addSubview:button];
119 rect->origin.x += kButtonWidth;
126 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
129 void Shell::PlatformExit() {
132 void Shell::PlatformCleanUp() {
135 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
145 id = IDC_NAV_FORWARD;
151 NOTREACHED() << "Unknown UI control";
154 [[[window_ contentView] viewWithTag:id] setEnabled:is_enabled];
157 void Shell::PlatformSetAddressBarURL(const GURL& url) {
161 NSString* url_string = base::SysUTF8ToNSString(url.spec());
162 [url_edit_view_ setStringValue:url_string];
165 void Shell::PlatformSetIsLoading(bool loading) {
168 void Shell::PlatformCreateWindow(int width, int height) {
170 content_size_ = gfx::Size(width, height);
174 NSRect initial_window_bounds =
175 NSMakeRect(0, 0, width, height + kURLBarHeight);
176 NSRect content_rect = initial_window_bounds;
177 NSUInteger style_mask = NSTitledWindowMask |
178 NSClosableWindowMask |
179 NSMiniaturizableWindowMask |
180 NSResizableWindowMask;
181 CrShellWindow* window =
182 [[CrShellWindow alloc] initWithContentRect:content_rect
184 backing:NSBackingStoreBuffered
187 [window setShell:this];
188 [window_ setTitle:kWindowTitle];
189 NSView* content = [window_ contentView];
191 // If the window is allowed to get too small, it will wreck the view bindings.
192 NSSize min_size = NSMakeSize(kMinimumWindowWidth, kMinimumWindowHeight);
193 min_size = [content convertSize:min_size toView:nil];
194 // Note that this takes window coordinates.
195 [window_ setContentMinSize:min_size];
197 // Set the shell window to participate in Lion Fullscreen mode. Set
198 // Setting this flag has no effect on Snow Leopard or earlier.
199 NSUInteger collectionBehavior = [window_ collectionBehavior];
200 collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
201 [window_ setCollectionBehavior:collectionBehavior];
203 // Rely on the window delegate to clean us up rather than immediately
204 // releasing when the window gets closed. We use the delegate to do
205 // everything from the autorelease pool so the shell isn't on the stack
206 // during cleanup (ie, a window close from javascript).
207 [window_ setReleasedWhenClosed:NO];
209 // Create a window delegate to watch for when it's asked to go away. It will
210 // clean itself up so we don't need to hold a reference.
211 ContentShellWindowDelegate* delegate =
212 [[ContentShellWindowDelegate alloc] initWithShell:this];
213 [window_ setDelegate:delegate];
215 NSRect button_frame =
216 NSMakeRect(0, NSMaxY(initial_window_bounds) - kURLBarHeight,
217 kButtonWidth, kURLBarHeight);
219 MakeShellButton(&button_frame, @"Back", content, IDC_NAV_BACK,
220 (NSView*)delegate, @"[", NSCommandKeyMask);
221 MakeShellButton(&button_frame, @"Forward", content, IDC_NAV_FORWARD,
222 (NSView*)delegate, @"]", NSCommandKeyMask);
223 MakeShellButton(&button_frame, @"Reload", content, IDC_NAV_RELOAD,
224 (NSView*)delegate, @"r", NSCommandKeyMask);
225 MakeShellButton(&button_frame, @"Stop", content, IDC_NAV_STOP,
226 (NSView*)delegate, @".", NSCommandKeyMask);
228 button_frame.size.width =
229 NSWidth(initial_window_bounds) - NSMinX(button_frame);
230 base::scoped_nsobject<NSTextField> url_edit_view(
231 [[NSTextField alloc] initWithFrame:button_frame]);
232 [content addSubview:url_edit_view];
233 [url_edit_view setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
234 [url_edit_view setTarget:delegate];
235 [url_edit_view setAction:@selector(takeURLStringValueFrom:)];
236 [[url_edit_view cell] setWraps:NO];
237 [[url_edit_view cell] setScrollable:YES];
238 url_edit_view_ = url_edit_view.get();
241 [window_ makeKeyAndOrderFront:nil];
244 void Shell::PlatformSetContents() {
245 NSView* web_view = web_contents_->GetNativeView();
246 [web_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
249 SizeTo(content_size_);
253 NSView* content = [window_ contentView];
254 [content addSubview:web_view];
256 NSRect frame = [content bounds];
257 frame.size.height -= kURLBarHeight;
258 [web_view setFrame:frame];
259 [web_view setNeedsDisplay:YES];
262 void Shell::SizeTo(const gfx::Size& content_size) {
264 NSRect frame = NSMakeRect(
265 0, 0, content_size.width(), content_size.height() + kURLBarHeight);
266 [window().contentView setFrame:frame];
269 NSView* web_view = web_contents_->GetNativeView();
270 NSRect frame = NSMakeRect(0, 0, content_size.width(), content_size.height());
271 [web_view setFrame:frame];
274 void Shell::PlatformResizeSubViews() {
275 // Not needed; subviews are bound.
278 void Shell::PlatformSetTitle(const base::string16& title) {
282 NSString* title_string = base::SysUTF16ToNSString(title);
283 [window_ setTitle:title_string];
286 bool Shell::PlatformHandleContextMenu(
287 const content::ContextMenuParams& params) {
291 void Shell::Close() {
295 [window_ performClose:nil];
298 void Shell::ActionPerformed(int control) {
303 case IDC_NAV_FORWARD:
315 void Shell::URLEntered(std::string url_string) {
316 if (!url_string.empty()) {
317 GURL url(url_string);
318 if (!url.has_scheme())
319 url = GURL("http://" + url_string);
324 void Shell::HandleKeyboardEvent(WebContents* source,
325 const NativeWebKeyboardEvent& event) {
326 if (event.skip_in_browser)
329 // The event handling to get this strictly right is a tangle; cheat here a bit
330 // by just letting the menus have a chance at it.
331 if ([event.os_event type] == NSKeyDown) {
332 if (([event.os_event modifierFlags] & NSCommandKeyMask) &&
333 [[event.os_event characters] isEqual:@"l"]) {
334 [window_ makeFirstResponder:url_edit_view_];
338 [[NSApp mainMenu] performKeyEquivalent:event.os_event];
342 } // namespace content