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/mac/sdk_forward_declarations.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "content/public/browser/native_web_keyboard_event.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/shell/app/resource.h"
17 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
20 // Receives notification that the window is closing so that it can start the
21 // tear-down process. Is responsible for deleting itself when done.
22 @interface ContentShellWindowDelegate : NSObject<NSWindowDelegate> {
24 content::Shell* shell_;
26 - (id)initWithShell:(content::Shell*)shell;
29 @implementation ContentShellWindowDelegate
31 - (id)initWithShell:(content::Shell*)shell {
32 if ((self = [super init])) {
38 // Called when the window is about to close. Perform the self-destruction
39 // sequence by getting rid of the shell and removing it and the window from
40 // the various global lists. By returning YES, we allow the window to be
41 // removed from the screen.
42 - (BOOL)windowShouldClose:(id)window {
50 - (void)performAction:(id)sender {
51 shell_->ActionPerformed([sender tag]);
54 - (void)takeURLStringValueFrom:(id)sender {
55 shell_->URLEntered(base::SysNSStringToUTF8([sender stringValue]));
60 @interface CrShellWindow : UnderlayOpenGLHostingWindow {
62 content::Shell* shell_;
64 - (void)setShell:(content::Shell*)shell;
65 - (void)showDevTools:(id)sender;
68 @implementation CrShellWindow
70 - (void)setShell:(content::Shell*)shell {
74 - (void)showDevTools:(id)sender {
75 shell_->ShowDevTools();
82 NSString* kWindowTitle = @"Content Shell";
84 // Layout constants (in view coordinates)
85 const CGFloat kButtonWidth = 72;
86 const CGFloat kURLBarHeight = 24;
88 // The minimum size of the window's content (in view coordinates)
89 const CGFloat kMinimumWindowWidth = 400;
90 const CGFloat kMinimumWindowHeight = 300;
92 void MakeShellButton(NSRect* rect,
98 NSUInteger modifier) {
99 base::scoped_nsobject<NSButton> button(
100 [[NSButton alloc] initWithFrame:*rect]);
101 [button setTitle:title];
102 [button setBezelStyle:NSSmallSquareBezelStyle];
103 [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)];
104 [button setTarget:target];
105 [button setAction:@selector(performAction:)];
106 [button setTag:control];
107 [button setKeyEquivalent:key];
108 [button setKeyEquivalentModifierMask:modifier];
109 [parent addSubview:button];
110 rect->origin.x += kButtonWidth;
117 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
120 void Shell::PlatformExit() {
123 void Shell::PlatformCleanUp() {
126 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
136 id = IDC_NAV_FORWARD;
142 NOTREACHED() << "Unknown UI control";
145 [[[window_ contentView] viewWithTag:id] setEnabled:is_enabled];
148 void Shell::PlatformSetAddressBarURL(const GURL& url) {
152 NSString* url_string = base::SysUTF8ToNSString(url.spec());
153 [url_edit_view_ setStringValue:url_string];
156 void Shell::PlatformSetIsLoading(bool loading) {
159 void Shell::PlatformCreateWindow(int width, int height) {
161 content_size_ = gfx::Size(width, height);
165 NSRect initial_window_bounds =
166 NSMakeRect(0, 0, width, height + kURLBarHeight);
167 NSRect content_rect = initial_window_bounds;
168 NSUInteger style_mask = NSTitledWindowMask |
169 NSClosableWindowMask |
170 NSMiniaturizableWindowMask |
171 NSResizableWindowMask;
172 CrShellWindow* window =
173 [[CrShellWindow alloc] initWithContentRect:content_rect
175 backing:NSBackingStoreBuffered
178 [window setShell:this];
179 [window_ setTitle:kWindowTitle];
180 NSView* content = [window_ contentView];
182 // If the window is allowed to get too small, it will wreck the view bindings.
183 NSSize min_size = NSMakeSize(kMinimumWindowWidth, kMinimumWindowHeight);
184 min_size = [content convertSize:min_size toView:nil];
185 // Note that this takes window coordinates.
186 [window_ setContentMinSize:min_size];
188 // Set the shell window to participate in Lion Fullscreen mode. Set
189 // Setting this flag has no effect on Snow Leopard or earlier.
190 NSUInteger collectionBehavior = [window_ collectionBehavior];
191 collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
192 [window_ setCollectionBehavior:collectionBehavior];
194 // Rely on the window delegate to clean us up rather than immediately
195 // releasing when the window gets closed. We use the delegate to do
196 // everything from the autorelease pool so the shell isn't on the stack
197 // during cleanup (ie, a window close from javascript).
198 [window_ setReleasedWhenClosed:NO];
200 // Create a window delegate to watch for when it's asked to go away. It will
201 // clean itself up so we don't need to hold a reference.
202 ContentShellWindowDelegate* delegate =
203 [[ContentShellWindowDelegate alloc] initWithShell:this];
204 [window_ setDelegate:delegate];
206 NSRect button_frame =
207 NSMakeRect(0, NSMaxY(initial_window_bounds) - kURLBarHeight,
208 kButtonWidth, kURLBarHeight);
210 MakeShellButton(&button_frame, @"Back", content, IDC_NAV_BACK,
211 (NSView*)delegate, @"[", NSCommandKeyMask);
212 MakeShellButton(&button_frame, @"Forward", content, IDC_NAV_FORWARD,
213 (NSView*)delegate, @"]", NSCommandKeyMask);
214 MakeShellButton(&button_frame, @"Reload", content, IDC_NAV_RELOAD,
215 (NSView*)delegate, @"r", NSCommandKeyMask);
216 MakeShellButton(&button_frame, @"Stop", content, IDC_NAV_STOP,
217 (NSView*)delegate, @".", NSCommandKeyMask);
219 button_frame.size.width =
220 NSWidth(initial_window_bounds) - NSMinX(button_frame);
221 base::scoped_nsobject<NSTextField> url_edit_view(
222 [[NSTextField alloc] initWithFrame:button_frame]);
223 [content addSubview:url_edit_view];
224 [url_edit_view setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
225 [url_edit_view setTarget:delegate];
226 [url_edit_view setAction:@selector(takeURLStringValueFrom:)];
227 [[url_edit_view cell] setWraps:NO];
228 [[url_edit_view cell] setScrollable:YES];
229 url_edit_view_ = url_edit_view.get();
232 [window_ makeKeyAndOrderFront:nil];
235 void Shell::PlatformSetContents() {
236 NSView* web_view = web_contents_->GetNativeView();
237 [web_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
240 SizeTo(content_size_);
244 NSView* content = [window_ contentView];
245 [content addSubview:web_view];
247 NSRect frame = [content bounds];
248 frame.size.height -= kURLBarHeight;
249 [web_view setFrame:frame];
250 [web_view setNeedsDisplay:YES];
253 void Shell::SizeTo(const gfx::Size& content_size) {
255 NSRect frame = NSMakeRect(
256 0, 0, content_size.width(), content_size.height() + kURLBarHeight);
257 [window().contentView setFrame:frame];
260 NSView* web_view = web_contents_->GetNativeView();
261 NSRect frame = NSMakeRect(0, 0, content_size.width(), content_size.height());
262 [web_view setFrame:frame];
265 void Shell::PlatformResizeSubViews() {
266 // Not needed; subviews are bound.
269 void Shell::PlatformSetTitle(const base::string16& title) {
273 NSString* title_string = base::SysUTF16ToNSString(title);
274 [window_ setTitle:title_string];
277 bool Shell::PlatformHandleContextMenu(
278 const content::ContextMenuParams& params) {
282 #if defined(TOOLKIT_VIEWS)
283 void Shell::PlatformWebContentsFocused(WebContents* contents) {
292 void Shell::Close() {
296 [window_ performClose:nil];
299 void Shell::ActionPerformed(int control) {
304 case IDC_NAV_FORWARD:
316 void Shell::URLEntered(std::string url_string) {
317 if (!url_string.empty()) {
318 GURL url(url_string);
319 if (!url.has_scheme())
320 url = GURL("http://" + url_string);
325 void Shell::HandleKeyboardEvent(WebContents* source,
326 const NativeWebKeyboardEvent& event) {
327 if (event.skip_in_browser)
330 // The event handling to get this strictly right is a tangle; cheat here a bit
331 // by just letting the menus have a chance at it.
332 if ([event.os_event type] == NSKeyDown) {
333 if (([event.os_event modifierFlags] & NSCommandKeyMask) &&
334 [[event.os_event characters] isEqual:@"l"]) {
335 [window_ makeFirstResponder:url_edit_view_];
339 [[NSApp mainMenu] performKeyEquivalent:event.os_event];
343 } // namespace content