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