• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2// reserved. Use of this source code is governed by a BSD-style license that
3// can be found in the LICENSE file.
4
5#include "tests/cefclient/browser/root_window_mac.h"
6
7#include <Cocoa/Cocoa.h>
8
9#include "include/base/cef_callback.h"
10#include "include/cef_app.h"
11#include "include/cef_application_mac.h"
12#include "tests/cefclient/browser/browser_window_osr_mac.h"
13#include "tests/cefclient/browser/browser_window_std_mac.h"
14#include "tests/cefclient/browser/main_context.h"
15#include "tests/cefclient/browser/temp_window.h"
16#include "tests/cefclient/browser/window_test_runner_mac.h"
17#include "tests/shared/browser/main_message_loop.h"
18#include "tests/shared/common/client_switches.h"
19
20// Receives notifications from controls and the browser window. Will delete
21// itself when done.
22@interface RootWindowDelegate : NSObject <NSWindowDelegate> {
23 @private
24  NSWindow* window_;
25  client::RootWindowMac* root_window_;
26  bool force_close_;
27}
28
29@property(nonatomic, readonly) client::RootWindowMac* root_window;
30@property(nonatomic, readwrite) bool force_close;
31
32- (id)initWithWindow:(NSWindow*)window
33       andRootWindow:(client::RootWindowMac*)root_window;
34- (IBAction)goBack:(id)sender;
35- (IBAction)goForward:(id)sender;
36- (IBAction)reload:(id)sender;
37- (IBAction)stopLoading:(id)sender;
38- (IBAction)takeURLStringValueFrom:(NSTextField*)sender;
39@end  // @interface RootWindowDelegate
40
41namespace client {
42
43namespace {
44
45// Sizes for URL bar layout.
46#define BUTTON_HEIGHT 22
47#define BUTTON_WIDTH 72
48#define BUTTON_MARGIN 8
49#define URLBAR_HEIGHT 32
50
51NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
52  NSButton* button = [[NSButton alloc] initWithFrame:*rect];
53#if !__has_feature(objc_arc)
54  [button autorelease];
55#endif  // !__has_feature(objc_arc)
56  [button setTitle:title];
57  [button setBezelStyle:NSSmallSquareBezelStyle];
58  [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)];
59  [parent addSubview:button];
60  rect->origin.x += BUTTON_WIDTH;
61  return button;
62}
63
64NSRect GetScreenRectForWindow(NSWindow* window) {
65  NSScreen* screen = [window screen];
66  if (screen == nil)
67    screen = [NSScreen mainScreen];
68  return [screen visibleFrame];
69}
70
71}  // namespace
72
73class RootWindowMacImpl
74    : public base::RefCountedThreadSafe<RootWindowMacImpl, DeleteOnMainThread> {
75 public:
76  RootWindowMacImpl(RootWindowMac& root_window);
77  ~RootWindowMacImpl();
78
79  // Called by RootWindowDelegate after the associated NSWindow has been
80  // closed.
81  void OnNativeWindowClosed();
82
83  void CreateBrowserWindow(const std::string& startup_url);
84  void CreateRootWindow(const CefBrowserSettings& settings,
85                        bool initially_hidden);
86
87  // RootWindow methods.
88  void Init(RootWindow::Delegate* delegate,
89            std::unique_ptr<RootWindowConfig> config,
90            const CefBrowserSettings& settings);
91  void InitAsPopup(RootWindow::Delegate* delegate,
92                   bool with_controls,
93                   bool with_osr,
94                   const CefPopupFeatures& popupFeatures,
95                   CefWindowInfo& windowInfo,
96                   CefRefPtr<CefClient>& client,
97                   CefBrowserSettings& settings);
98  void Show(RootWindow::ShowMode mode);
99  void Hide();
100  void SetBounds(int x, int y, size_t width, size_t height);
101  void Close(bool force);
102  void SetDeviceScaleFactor(float device_scale_factor);
103  float GetDeviceScaleFactor() const;
104  CefRefPtr<CefBrowser> GetBrowser() const;
105  ClientWindowHandle GetWindowHandle() const;
106  bool WithWindowlessRendering() const;
107  bool WithExtension() const;
108
109  // BrowserWindow::Delegate methods.
110  void OnBrowserCreated(CefRefPtr<CefBrowser> browser);
111  void OnBrowserWindowDestroyed();
112  void OnSetAddress(const std::string& url);
113  void OnSetTitle(const std::string& title);
114  void OnSetFullscreen(bool fullscreen);
115  void OnAutoResize(const CefSize& new_size);
116  void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
117  void OnSetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
118
119  void NotifyDestroyedIfDone();
120
121  // After initialization all members are only accessed on the main thread.
122  // Members set during initialization.
123  RootWindowMac& root_window_;
124  bool with_controls_;
125  bool with_osr_;
126  bool with_extension_;
127  bool is_popup_;
128  CefRect start_rect_;
129  std::unique_ptr<BrowserWindow> browser_window_;
130  bool initialized_;
131
132  // Main window.
133  NSWindow* window_;
134  RootWindowDelegate* window_delegate_;
135
136  // Buttons.
137  NSButton* back_button_;
138  NSButton* forward_button_;
139  NSButton* reload_button_;
140  NSButton* stop_button_;
141
142  // URL text field.
143  NSTextField* url_textfield_;
144
145  bool window_destroyed_;
146  bool browser_destroyed_;
147};
148
149RootWindowMacImpl::RootWindowMacImpl(RootWindowMac& root_window)
150    : root_window_(root_window),
151      with_controls_(false),
152      with_osr_(false),
153      is_popup_(false),
154      initialized_(false),
155      window_(nil),
156      back_button_(nil),
157      forward_button_(nil),
158      reload_button_(nil),
159      stop_button_(nil),
160      url_textfield_(nil),
161      window_destroyed_(false),
162      browser_destroyed_(false) {}
163
164RootWindowMacImpl::~RootWindowMacImpl() {
165  REQUIRE_MAIN_THREAD();
166
167  // The window and browser should already have been destroyed.
168  DCHECK(window_destroyed_);
169  DCHECK(browser_destroyed_);
170}
171
172void RootWindowMacImpl::Init(RootWindow::Delegate* delegate,
173                             std::unique_ptr<RootWindowConfig> config,
174                             const CefBrowserSettings& settings) {
175  DCHECK(!initialized_);
176
177  with_controls_ = config->with_controls;
178  with_osr_ = config->with_osr;
179  with_extension_ = config->with_extension;
180  start_rect_ = config->bounds;
181
182  CreateBrowserWindow(config->url);
183
184  initialized_ = true;
185
186  // Create the native root window on the main thread.
187  if (CURRENTLY_ON_MAIN_THREAD()) {
188    CreateRootWindow(settings, config->initially_hidden);
189  } else {
190    MAIN_POST_CLOSURE(base::BindOnce(&RootWindowMacImpl::CreateRootWindow, this,
191                                     settings, config->initially_hidden));
192  }
193}
194
195void RootWindowMacImpl::InitAsPopup(RootWindow::Delegate* delegate,
196                                    bool with_controls,
197                                    bool with_osr,
198                                    const CefPopupFeatures& popupFeatures,
199                                    CefWindowInfo& windowInfo,
200                                    CefRefPtr<CefClient>& client,
201                                    CefBrowserSettings& settings) {
202  DCHECK(delegate);
203  DCHECK(!initialized_);
204
205  with_controls_ = with_controls;
206  with_osr_ = with_osr;
207  is_popup_ = true;
208
209  if (popupFeatures.xSet)
210    start_rect_.x = popupFeatures.x;
211  if (popupFeatures.ySet)
212    start_rect_.y = popupFeatures.y;
213  if (popupFeatures.widthSet)
214    start_rect_.width = popupFeatures.width;
215  if (popupFeatures.heightSet)
216    start_rect_.height = popupFeatures.height;
217
218  CreateBrowserWindow(std::string());
219
220  initialized_ = true;
221
222  // The new popup is initially parented to a temporary window. The native root
223  // window will be created after the browser is created and the popup window
224  // will be re-parented to it at that time.
225  browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
226                                  client, settings);
227}
228
229void RootWindowMacImpl::Show(RootWindow::ShowMode mode) {
230  REQUIRE_MAIN_THREAD();
231
232  if (!window_)
233    return;
234
235  const bool is_visible = [window_ isVisible];
236  const bool is_minimized = [window_ isMiniaturized];
237  const bool is_maximized = [window_ isZoomed];
238
239  if ((mode == RootWindow::ShowMinimized && is_minimized) ||
240      (mode == RootWindow::ShowMaximized && is_maximized) ||
241      (mode == RootWindow::ShowNormal && is_visible)) {
242    // The window is already in the desired state.
243    return;
244  }
245
246  // Undo the previous state since it's not the desired state.
247  if (is_minimized)
248    [window_ deminiaturize:nil];
249  else if (is_maximized)
250    [window_ performZoom:nil];
251
252  // Window visibility may change after (for example) deminiaturizing the
253  // window.
254  if (![window_ isVisible])
255    [window_ makeKeyAndOrderFront:nil];
256
257  if (mode == RootWindow::ShowMinimized)
258    [window_ performMiniaturize:nil];
259  else if (mode == RootWindow::ShowMaximized)
260    [window_ performZoom:nil];
261}
262
263void RootWindowMacImpl::Hide() {
264  REQUIRE_MAIN_THREAD();
265
266  if (!window_)
267    return;
268
269  // Undo miniaturization, if any, so the window will actually be hidden.
270  if ([window_ isMiniaturized])
271    [window_ deminiaturize:nil];
272
273  // Hide the window.
274  [window_ orderOut:nil];
275}
276
277void RootWindowMacImpl::SetBounds(int x, int y, size_t width, size_t height) {
278  REQUIRE_MAIN_THREAD();
279
280  if (!window_)
281    return;
282
283  NSRect screen_rect = GetScreenRectForWindow(window_);
284
285  // Desired content rectangle.
286  NSRect content_rect;
287  content_rect.size.width = static_cast<int>(width);
288  content_rect.size.height =
289      static_cast<int>(height) + (with_controls_ ? URLBAR_HEIGHT : 0);
290
291  // Convert to a frame rectangle.
292  NSRect frame_rect = [window_ frameRectForContentRect:content_rect];
293  frame_rect.origin.x = x;
294  frame_rect.origin.y = screen_rect.size.height - y;
295
296  [window_ setFrame:frame_rect display:YES];
297}
298
299void RootWindowMacImpl::Close(bool force) {
300  REQUIRE_MAIN_THREAD();
301
302  if (window_) {
303    static_cast<RootWindowDelegate*>([window_ delegate]).force_close = force;
304    [window_ performClose:nil];
305    window_destroyed_ = true;
306  }
307}
308
309void RootWindowMacImpl::SetDeviceScaleFactor(float device_scale_factor) {
310  REQUIRE_MAIN_THREAD();
311
312  if (browser_window_ && with_osr_)
313    browser_window_->SetDeviceScaleFactor(device_scale_factor);
314}
315
316float RootWindowMacImpl::GetDeviceScaleFactor() const {
317  REQUIRE_MAIN_THREAD();
318
319  if (browser_window_ && with_osr_)
320    return browser_window_->GetDeviceScaleFactor();
321
322  NOTREACHED();
323  return 0.0f;
324}
325
326CefRefPtr<CefBrowser> RootWindowMacImpl::GetBrowser() const {
327  REQUIRE_MAIN_THREAD();
328
329  if (browser_window_)
330    return browser_window_->GetBrowser();
331  return nullptr;
332}
333
334ClientWindowHandle RootWindowMacImpl::GetWindowHandle() const {
335  REQUIRE_MAIN_THREAD();
336  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE([window_ contentView]);
337}
338
339bool RootWindowMacImpl::WithWindowlessRendering() const {
340  REQUIRE_MAIN_THREAD();
341  return with_osr_;
342}
343
344bool RootWindowMacImpl::WithExtension() const {
345  REQUIRE_MAIN_THREAD();
346  return with_extension_;
347}
348
349void RootWindowMacImpl::OnNativeWindowClosed() {
350  window_ = nil;
351  window_destroyed_ = true;
352  NotifyDestroyedIfDone();
353}
354
355void RootWindowMacImpl::CreateBrowserWindow(const std::string& startup_url) {
356  if (with_osr_) {
357    OsrRendererSettings settings = {};
358    MainContext::Get()->PopulateOsrSettings(&settings);
359    browser_window_.reset(
360        new BrowserWindowOsrMac(&root_window_, startup_url, settings));
361  } else {
362    browser_window_.reset(new BrowserWindowStdMac(&root_window_, startup_url));
363  }
364}
365
366void RootWindowMacImpl::CreateRootWindow(const CefBrowserSettings& settings,
367                                         bool initially_hidden) {
368  REQUIRE_MAIN_THREAD();
369  DCHECK(!window_);
370
371  // TODO(port): If no x,y position is specified the window will always appear
372  // in the upper-left corner. Maybe there's a better default place to put it?
373  int x = start_rect_.x;
374  int y = start_rect_.y;
375  int width, height;
376  if (start_rect_.IsEmpty()) {
377    // TODO(port): Also, maybe there's a better way to choose the default size.
378    width = 800;
379    height = 600;
380  } else {
381    width = start_rect_.width;
382    height = start_rect_.height;
383  }
384
385  // Create the main window.
386  NSRect screen_rect = [[NSScreen mainScreen] visibleFrame];
387  NSRect window_rect =
388      NSMakeRect(x, screen_rect.size.height - y, width, height);
389
390  // The CEF framework library is loaded at runtime so we need to use this
391  // mechanism for retrieving the class.
392  Class window_class = NSClassFromString(@"UnderlayOpenGLHostingWindow");
393  CHECK(window_class);
394
395  window_ = [[window_class alloc]
396      initWithContentRect:window_rect
397                styleMask:(NSTitledWindowMask | NSClosableWindowMask |
398                           NSMiniaturizableWindowMask | NSResizableWindowMask |
399                           NSUnifiedTitleAndToolbarWindowMask)
400                  backing:NSBackingStoreBuffered
401                    defer:NO];
402  [window_ setTitle:@"cefclient"];
403  // No dark mode, please
404  window_.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
405
406  // Create the delegate for control and browser window events.
407  window_delegate_ = [[RootWindowDelegate alloc] initWithWindow:window_
408                                                  andRootWindow:&root_window_];
409
410  // Rely on the window delegate to clean us up rather than immediately
411  // releasing when the window gets closed. We use the delegate to do
412  // everything from the autorelease pool so the window isn't on the stack
413  // during cleanup (ie, a window close from javascript).
414  [window_ setReleasedWhenClosed:NO];
415
416  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
417  [window_
418      setBackgroundColor:[NSColor
419                             colorWithCalibratedRed:float(CefColorGetR(
420                                                        background_color)) /
421                                                    255.0f
422                                              green:float(CefColorGetG(
423                                                        background_color)) /
424                                                    255.0f
425                                               blue:float(CefColorGetB(
426                                                        background_color)) /
427                                                    255.0f
428                                              alpha:1.f]];
429
430  NSView* contentView = [window_ contentView];
431  NSRect contentBounds = [contentView bounds];
432
433  if (!with_osr_) {
434    // Make the content view for the window have a layer. This will make all
435    // sub-views have layers. This is necessary to ensure correct layer
436    // ordering of all child views and their layers.
437    [contentView setWantsLayer:YES];
438  }
439
440  if (with_controls_) {
441    // Create the buttons.
442    NSRect button_rect = contentBounds;
443    button_rect.origin.y = window_rect.size.height - URLBAR_HEIGHT +
444                           (URLBAR_HEIGHT - BUTTON_HEIGHT) / 2;
445    button_rect.size.height = BUTTON_HEIGHT;
446    button_rect.origin.x += BUTTON_MARGIN;
447    button_rect.size.width = BUTTON_WIDTH;
448
449    contentBounds.size.height -= URLBAR_HEIGHT;
450
451    back_button_ = MakeButton(&button_rect, @"Back", contentView);
452    [back_button_ setTarget:window_delegate_];
453    [back_button_ setAction:@selector(goBack:)];
454    [back_button_ setEnabled:NO];
455
456    forward_button_ = MakeButton(&button_rect, @"Forward", contentView);
457    [forward_button_ setTarget:window_delegate_];
458    [forward_button_ setAction:@selector(goForward:)];
459    [forward_button_ setEnabled:NO];
460
461    reload_button_ = MakeButton(&button_rect, @"Reload", contentView);
462    [reload_button_ setTarget:window_delegate_];
463    [reload_button_ setAction:@selector(reload:)];
464    [reload_button_ setEnabled:NO];
465
466    stop_button_ = MakeButton(&button_rect, @"Stop", contentView);
467    [stop_button_ setTarget:window_delegate_];
468    [stop_button_ setAction:@selector(stopLoading:)];
469    [stop_button_ setEnabled:NO];
470
471    // Create the URL text field.
472    button_rect.origin.x += BUTTON_MARGIN;
473    button_rect.size.width =
474        [contentView bounds].size.width - button_rect.origin.x - BUTTON_MARGIN;
475    url_textfield_ = [[NSTextField alloc] initWithFrame:button_rect];
476    [contentView addSubview:url_textfield_];
477    [url_textfield_
478        setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
479    [url_textfield_ setTarget:window_delegate_];
480    [url_textfield_ setAction:@selector(takeURLStringValueFrom:)];
481    [url_textfield_ setEnabled:NO];
482    [[url_textfield_ cell] setWraps:NO];
483    [[url_textfield_ cell] setScrollable:YES];
484  }
485
486  if (!is_popup_) {
487    // Create the browser window.
488    browser_window_->CreateBrowser(
489        CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView),
490        CefRect(0, 0, width, height), settings, nullptr,
491        root_window_.delegate_->GetRequestContext(&root_window_));
492  } else {
493    // With popups we already have a browser window. Parent the browser window
494    // to the root window and show it in the correct location.
495    browser_window_->ShowPopup(CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView), 0,
496                               0, contentBounds.size.width,
497                               contentBounds.size.height);
498  }
499
500  if (!initially_hidden) {
501    // Show the window.
502    Show(RootWindow::ShowNormal);
503
504    // Size the window.
505    SetBounds(x, y, width, height);
506  }
507}
508
509void RootWindowMacImpl::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
510  REQUIRE_MAIN_THREAD();
511
512  // For popup browsers create the root window once the browser has been
513  // created.
514  if (is_popup_)
515    CreateRootWindow(CefBrowserSettings(), false);
516
517  root_window_.delegate_->OnBrowserCreated(&root_window_, browser);
518}
519
520void RootWindowMacImpl::OnBrowserWindowDestroyed() {
521  REQUIRE_MAIN_THREAD();
522
523  browser_window_.reset();
524
525  if (!window_destroyed_) {
526    // The browser was destroyed first. This could be due to the use of
527    // off-screen rendering or execution of JavaScript window.close().
528    // Close the RootWindow.
529    Close(true);
530  }
531
532  browser_destroyed_ = true;
533  NotifyDestroyedIfDone();
534}
535
536void RootWindowMacImpl::OnSetAddress(const std::string& url) {
537  REQUIRE_MAIN_THREAD();
538
539  if (url_textfield_) {
540    std::string urlStr(url);
541    NSString* str = [NSString stringWithUTF8String:urlStr.c_str()];
542    [url_textfield_ setStringValue:str];
543  }
544}
545
546void RootWindowMacImpl::OnSetDraggableRegions(
547    const std::vector<CefDraggableRegion>& regions) {
548  REQUIRE_MAIN_THREAD();
549  // TODO(cef): Implement support for draggable regions on this platform.
550}
551
552void RootWindowMacImpl::OnSetTitle(const std::string& title) {
553  REQUIRE_MAIN_THREAD();
554
555  if (window_) {
556    std::string titleStr(title);
557    NSString* str = [NSString stringWithUTF8String:titleStr.c_str()];
558    [window_ setTitle:str];
559  }
560}
561
562void RootWindowMacImpl::OnSetFullscreen(bool fullscreen) {
563  REQUIRE_MAIN_THREAD();
564
565  CefRefPtr<CefBrowser> browser = GetBrowser();
566  if (browser) {
567    std::unique_ptr<window_test::WindowTestRunnerMac> test_runner(
568        new window_test::WindowTestRunnerMac());
569    if (fullscreen)
570      test_runner->Maximize(browser);
571    else
572      test_runner->Restore(browser);
573  }
574}
575
576void RootWindowMacImpl::OnAutoResize(const CefSize& new_size) {
577  REQUIRE_MAIN_THREAD();
578
579  if (!window_)
580    return;
581
582  // Desired content rectangle.
583  NSRect content_rect;
584  content_rect.size.width = static_cast<int>(new_size.width);
585  content_rect.size.height =
586      static_cast<int>(new_size.height) + (with_controls_ ? URLBAR_HEIGHT : 0);
587
588  // Convert to a frame rectangle.
589  NSRect frame_rect = [window_ frameRectForContentRect:content_rect];
590  // Don't change the origin.
591  frame_rect.origin = window_.frame.origin;
592
593  [window_ setFrame:frame_rect display:YES];
594
595  // Make sure the window is visible.
596  Show(RootWindow::ShowNormal);
597}
598
599void RootWindowMacImpl::OnSetLoadingState(bool isLoading,
600                                          bool canGoBack,
601                                          bool canGoForward) {
602  REQUIRE_MAIN_THREAD();
603
604  if (with_controls_) {
605    [url_textfield_ setEnabled:YES];
606    [reload_button_ setEnabled:!isLoading];
607    [stop_button_ setEnabled:isLoading];
608    [back_button_ setEnabled:canGoBack];
609    [forward_button_ setEnabled:canGoForward];
610  }
611
612  // After Loading is done, check if voiceover is running and accessibility
613  // should be enabled.
614  if (!isLoading) {
615    Boolean keyExists = false;
616    // On OSX there is no API to query if VoiceOver is active or not. The value
617    // however is stored in preferences that can be queried.
618    if (CFPreferencesGetAppBooleanValue(CFSTR("voiceOverOnOffKey"),
619                                        CFSTR("com.apple.universalaccess"),
620                                        &keyExists)) {
621      GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
622    }
623  }
624}
625
626void RootWindowMacImpl::NotifyDestroyedIfDone() {
627  // Notify once both the window and the browser have been destroyed.
628  if (window_destroyed_ && browser_destroyed_)
629    root_window_.delegate_->OnRootWindowDestroyed(&root_window_);
630}
631
632RootWindowMac::RootWindowMac() {
633  impl_ = new RootWindowMacImpl(*this);
634}
635
636RootWindowMac::~RootWindowMac() {}
637
638BrowserWindow* RootWindowMac::browser_window() const {
639  return impl_->browser_window_.get();
640}
641
642RootWindow::Delegate* RootWindowMac::delegate() const {
643  return delegate_;
644}
645
646void RootWindowMac::Init(RootWindow::Delegate* delegate,
647                         std::unique_ptr<RootWindowConfig> config,
648                         const CefBrowserSettings& settings) {
649  DCHECK(delegate);
650  delegate_ = delegate;
651  impl_->Init(delegate, std::move(config), settings);
652}
653
654void RootWindowMac::InitAsPopup(RootWindow::Delegate* delegate,
655                                bool with_controls,
656                                bool with_osr,
657                                const CefPopupFeatures& popupFeatures,
658                                CefWindowInfo& windowInfo,
659                                CefRefPtr<CefClient>& client,
660                                CefBrowserSettings& settings) {
661  DCHECK(delegate);
662  delegate_ = delegate;
663  impl_->InitAsPopup(delegate, with_controls, with_osr, popupFeatures,
664                     windowInfo, client, settings);
665}
666
667void RootWindowMac::Show(ShowMode mode) {
668  impl_->Show(mode);
669}
670
671void RootWindowMac::Hide() {
672  impl_->Hide();
673}
674
675void RootWindowMac::SetBounds(int x, int y, size_t width, size_t height) {
676  impl_->SetBounds(x, y, width, height);
677}
678
679void RootWindowMac::Close(bool force) {
680  impl_->Close(force);
681}
682
683void RootWindowMac::SetDeviceScaleFactor(float device_scale_factor) {
684  impl_->SetDeviceScaleFactor(device_scale_factor);
685}
686
687float RootWindowMac::GetDeviceScaleFactor() const {
688  return impl_->GetDeviceScaleFactor();
689}
690
691CefRefPtr<CefBrowser> RootWindowMac::GetBrowser() const {
692  return impl_->GetBrowser();
693}
694
695ClientWindowHandle RootWindowMac::GetWindowHandle() const {
696  return impl_->GetWindowHandle();
697}
698
699bool RootWindowMac::WithWindowlessRendering() const {
700  return impl_->WithWindowlessRendering();
701}
702
703bool RootWindowMac::WithExtension() const {
704  return impl_->WithExtension();
705}
706
707void RootWindowMac::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
708  impl_->OnBrowserCreated(browser);
709}
710
711void RootWindowMac::OnBrowserWindowDestroyed() {
712  impl_->OnBrowserWindowDestroyed();
713}
714
715void RootWindowMac::OnSetAddress(const std::string& url) {
716  impl_->OnSetAddress(url);
717}
718
719void RootWindowMac::OnSetTitle(const std::string& title) {
720  impl_->OnSetTitle(title);
721}
722
723void RootWindowMac::OnSetFullscreen(bool fullscreen) {
724  impl_->OnSetFullscreen(fullscreen);
725}
726
727void RootWindowMac::OnAutoResize(const CefSize& new_size) {
728  impl_->OnAutoResize(new_size);
729}
730
731void RootWindowMac::OnSetLoadingState(bool isLoading,
732                                      bool canGoBack,
733                                      bool canGoForward) {
734  impl_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
735}
736
737void RootWindowMac::OnSetDraggableRegions(
738    const std::vector<CefDraggableRegion>& regions) {
739  impl_->OnSetDraggableRegions(regions);
740}
741
742void RootWindowMac::OnNativeWindowClosed() {
743  impl_->OnNativeWindowClosed();
744}
745
746// static
747scoped_refptr<RootWindow> RootWindow::GetForNSWindow(NSWindow* window) {
748  RootWindowDelegate* delegate =
749      static_cast<RootWindowDelegate*>([window delegate]);
750  return [delegate root_window];
751}
752
753}  // namespace client
754
755@implementation RootWindowDelegate
756
757@synthesize root_window = root_window_;
758@synthesize force_close = force_close_;
759
760- (id)initWithWindow:(NSWindow*)window
761       andRootWindow:(client::RootWindowMac*)root_window {
762  if (self = [super init]) {
763    window_ = window;
764    [window_ setDelegate:self];
765    root_window_ = root_window;
766    force_close_ = false;
767
768    // Register for application hide/unhide notifications.
769    [[NSNotificationCenter defaultCenter]
770        addObserver:self
771           selector:@selector(applicationDidHide:)
772               name:NSApplicationDidHideNotification
773             object:nil];
774    [[NSNotificationCenter defaultCenter]
775        addObserver:self
776           selector:@selector(applicationDidUnhide:)
777               name:NSApplicationDidUnhideNotification
778             object:nil];
779  }
780  return self;
781}
782
783- (void)dealloc {
784  [[NSNotificationCenter defaultCenter] removeObserver:self];
785#if !__has_feature(objc_arc)
786  [super dealloc];
787#endif  // !__has_feature(objc_arc)
788}
789
790- (IBAction)goBack:(id)sender {
791  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
792  if (browser.get())
793    browser->GoBack();
794}
795
796- (IBAction)goForward:(id)sender {
797  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
798  if (browser.get())
799    browser->GoForward();
800}
801
802- (IBAction)reload:(id)sender {
803  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
804  if (browser.get())
805    browser->Reload();
806}
807
808- (IBAction)stopLoading:(id)sender {
809  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
810  if (browser.get())
811    browser->StopLoad();
812}
813
814- (IBAction)takeURLStringValueFrom:(NSTextField*)sender {
815  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
816  if (!browser.get())
817    return;
818
819  NSString* url = [sender stringValue];
820
821  // if it doesn't already have a prefix, add http. If we can't parse it,
822  // just don't bother rather than making things worse.
823  NSURL* tempUrl = [NSURL URLWithString:url];
824  if (tempUrl && ![tempUrl scheme])
825    url = [@"http://" stringByAppendingString:url];
826
827  std::string urlStr = [url UTF8String];
828  browser->GetMainFrame()->LoadURL(urlStr);
829}
830
831// Called when we are activated (when we gain focus).
832- (void)windowDidBecomeKey:(NSNotification*)notification {
833  client::BrowserWindow* browser_window = root_window_->browser_window();
834  if (browser_window)
835    browser_window->SetFocus(true);
836  root_window_->delegate()->OnRootWindowActivated(root_window_);
837}
838
839// Called when we are deactivated (when we lose focus).
840- (void)windowDidResignKey:(NSNotification*)notification {
841  client::BrowserWindow* browser_window = root_window_->browser_window();
842  if (browser_window)
843    browser_window->SetFocus(false);
844}
845
846// Called when we have been minimized.
847- (void)windowDidMiniaturize:(NSNotification*)notification {
848  client::BrowserWindow* browser_window = root_window_->browser_window();
849  if (browser_window)
850    browser_window->Hide();
851}
852
853// Called when we have been unminimized.
854- (void)windowDidDeminiaturize:(NSNotification*)notification {
855  client::BrowserWindow* browser_window = root_window_->browser_window();
856  if (browser_window)
857    browser_window->Show();
858}
859
860// Called when the application has been hidden.
861- (void)applicationDidHide:(NSNotification*)notification {
862  // If the window is miniaturized then nothing has really changed.
863  if (![window_ isMiniaturized]) {
864    client::BrowserWindow* browser_window = root_window_->browser_window();
865    if (browser_window)
866      browser_window->Hide();
867  }
868}
869
870// Called when the application has been unhidden.
871- (void)applicationDidUnhide:(NSNotification*)notification {
872  // If the window is miniaturized then nothing has really changed.
873  if (![window_ isMiniaturized]) {
874    client::BrowserWindow* browser_window = root_window_->browser_window();
875    if (browser_window)
876      browser_window->Show();
877  }
878}
879
880// Called when the window is about to close. Perform the self-destruction
881// sequence by getting rid of the window. By returning YES, we allow the window
882// to be removed from the screen.
883- (BOOL)windowShouldClose:(NSWindow*)window {
884  if (!force_close_) {
885    client::BrowserWindow* browser_window = root_window_->browser_window();
886    if (browser_window && !browser_window->IsClosing()) {
887      CefRefPtr<CefBrowser> browser = browser_window->GetBrowser();
888      if (browser.get()) {
889        // Notify the browser window that we would like to close it. This
890        // will result in a call to ClientHandler::DoClose() if the
891        // JavaScript 'onbeforeunload' event handler allows it.
892        browser->GetHost()->CloseBrowser(false);
893
894        // Cancel the close.
895        return NO;
896      }
897    }
898  }
899
900  // Clean ourselves up after clearing the stack of anything that might have the
901  // window on it.
902  [self cleanup];
903
904  // Allow the close.
905  return YES;
906}
907
908// Deletes itself.
909- (void)cleanup {
910  // Don't want any more delegate callbacks after we destroy ourselves.
911  window_.delegate = nil;
912  window_.contentView = [[NSView alloc] initWithFrame:NSZeroRect];
913  // Delete the window.
914#if !__has_feature(objc_arc)
915  [window_ autorelease];
916#endif  // !__has_feature(objc_arc)
917  window_ = nil;
918  root_window_->OnNativeWindowClosed();
919}
920
921@end  // @implementation RootWindowDelegate
922