• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 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/views_window.h"
6 
7 #include <algorithm>
8 
9 #include "include/base/cef_build.h"
10 #include "include/base/cef_callback.h"
11 #include "include/cef_app.h"
12 #include "include/cef_i18n_util.h"
13 #include "include/views/cef_box_layout.h"
14 #include "include/wrapper/cef_helpers.h"
15 #include "tests/cefclient/browser/main_context.h"
16 #include "tests/cefclient/browser/resource.h"
17 #include "tests/cefclient/browser/views_style.h"
18 #include "tests/shared/browser/extension_util.h"
19 #include "tests/shared/common/client_switches.h"
20 
21 #if !defined(OS_WIN)
22 #define VK_ESCAPE 0x1B
23 #define VK_RETURN 0x0D
24 #define VK_MENU 0x12  // ALT key.
25 #endif
26 
27 namespace client {
28 
29 namespace {
30 
31 const char kDefaultExtensionIcon[] = "window_icon";
32 
33 // Control IDs for Views in the top-level Window.
34 enum ControlIds {
35   ID_WINDOW = 1,
36   ID_BROWSER_VIEW,
37   ID_BACK_BUTTON,
38   ID_FORWARD_BUTTON,
39   ID_STOP_BUTTON,
40   ID_RELOAD_BUTTON,
41   ID_URL_TEXTFIELD,
42   ID_MENU_BUTTON,
43 
44   // Reserved range of top menu button IDs.
45   ID_TOP_MENU_FIRST,
46   ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10,
47 
48   // Reserved range of extension button IDs.
49   ID_EXTENSION_BUTTON_FIRST,
50   ID_EXTENSION_BUTTON_LAST = ID_EXTENSION_BUTTON_FIRST + 10,
51 };
52 
53 typedef std::vector<CefRefPtr<CefLabelButton>> LabelButtons;
54 
55 // Make all |buttons| the same size.
MakeButtonsSameSize(const LabelButtons & buttons)56 void MakeButtonsSameSize(const LabelButtons& buttons) {
57   CefSize size;
58 
59   // Determine the largest button size.
60   for (size_t i = 0U; i < buttons.size(); ++i) {
61     const CefSize& button_size = buttons[i]->GetPreferredSize();
62     if (size.width < button_size.width)
63       size.width = button_size.width;
64     if (size.height < button_size.height)
65       size.height = button_size.height;
66   }
67 
68   for (size_t i = 0U; i < buttons.size(); ++i) {
69     // Set the button's minimum size.
70     buttons[i]->SetMinimumSize(size);
71 
72     // Re-layout the button and all parent Views.
73     buttons[i]->InvalidateLayout();
74   }
75 }
76 
AddTestMenuItems(CefRefPtr<CefMenuModel> test_menu)77 void AddTestMenuItems(CefRefPtr<CefMenuModel> test_menu) {
78   test_menu->AddItem(ID_TESTS_GETSOURCE, "Get Source");
79   test_menu->AddItem(ID_TESTS_GETTEXT, "Get Text");
80   test_menu->AddItem(ID_TESTS_WINDOW_NEW, "New Window");
81   test_menu->AddItem(ID_TESTS_WINDOW_POPUP, "Popup Window");
82   test_menu->AddItem(ID_TESTS_REQUEST, "Request");
83   test_menu->AddItem(ID_TESTS_PLUGIN_INFO, "Plugin Info");
84   test_menu->AddItem(ID_TESTS_ZOOM_IN, "Zoom In");
85   test_menu->AddItem(ID_TESTS_ZOOM_OUT, "Zoom Out");
86   test_menu->AddItem(ID_TESTS_ZOOM_RESET, "Zoom Reset");
87   test_menu->AddItem(ID_TESTS_TRACING_BEGIN, "Begin Tracing");
88   test_menu->AddItem(ID_TESTS_TRACING_END, "End Tracing");
89   test_menu->AddItem(ID_TESTS_PRINT, "Print");
90   test_menu->AddItem(ID_TESTS_PRINT_TO_PDF, "Print to PDF");
91   test_menu->AddItem(ID_TESTS_MUTE_AUDIO, "Mute Audio");
92   test_menu->AddItem(ID_TESTS_UNMUTE_AUDIO, "Unmute Audio");
93   test_menu->AddItem(ID_TESTS_OTHER_TESTS, "Other Tests");
94 }
95 
AddFileMenuItems(CefRefPtr<CefMenuModel> file_menu)96 void AddFileMenuItems(CefRefPtr<CefMenuModel> file_menu) {
97   file_menu->AddItem(ID_QUIT, "E&xit");
98 
99   // Show the accelerator shortcut text in the menu.
100   file_menu->SetAcceleratorAt(file_menu->GetCount() - 1, 'X', false, false,
101                               true);
102 }
103 
104 }  // namespace
105 
106 // static
Create(Delegate * delegate,CefRefPtr<CefClient> client,const CefString & url,const CefBrowserSettings & settings,CefRefPtr<CefRequestContext> request_context)107 CefRefPtr<ViewsWindow> ViewsWindow::Create(
108     Delegate* delegate,
109     CefRefPtr<CefClient> client,
110     const CefString& url,
111     const CefBrowserSettings& settings,
112     CefRefPtr<CefRequestContext> request_context) {
113   CEF_REQUIRE_UI_THREAD();
114   DCHECK(delegate);
115 
116   // Create a new ViewsWindow.
117   CefRefPtr<ViewsWindow> views_window = new ViewsWindow(delegate, nullptr);
118 
119   // Create a new BrowserView.
120   CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
121       client, url, settings, nullptr, request_context, views_window);
122 
123   // Associate the BrowserView with the ViewsWindow.
124   views_window->SetBrowserView(browser_view);
125 
126   // Create a new top-level Window. It will show itself after creation.
127   CefWindow::CreateTopLevelWindow(views_window);
128 
129   return views_window;
130 }
131 
Show()132 void ViewsWindow::Show() {
133   CEF_REQUIRE_UI_THREAD();
134   if (window_)
135     window_->Show();
136   if (browser_view_ && !window_->IsMinimized()) {
137     // Give keyboard focus to the BrowserView.
138     browser_view_->RequestFocus();
139   }
140 }
141 
Hide()142 void ViewsWindow::Hide() {
143   CEF_REQUIRE_UI_THREAD();
144   if (window_)
145     window_->Hide();
146 }
147 
Minimize()148 void ViewsWindow::Minimize() {
149   CEF_REQUIRE_UI_THREAD();
150   if (window_)
151     window_->Minimize();
152 }
153 
Maximize()154 void ViewsWindow::Maximize() {
155   CEF_REQUIRE_UI_THREAD();
156   if (window_)
157     window_->Maximize();
158 }
159 
SetBounds(const CefRect & bounds)160 void ViewsWindow::SetBounds(const CefRect& bounds) {
161   CEF_REQUIRE_UI_THREAD();
162   if (window_)
163     window_->SetBounds(bounds);
164 }
165 
SetBrowserSize(const CefSize & size,bool has_position,const CefPoint & position)166 void ViewsWindow::SetBrowserSize(const CefSize& size,
167                                  bool has_position,
168                                  const CefPoint& position) {
169   CEF_REQUIRE_UI_THREAD();
170   if (browser_view_)
171     browser_view_->SetSize(size);
172   if (window_) {
173     window_->SizeToPreferredSize();
174     if (has_position)
175       window_->SetPosition(position);
176   }
177 }
178 
Close(bool force)179 void ViewsWindow::Close(bool force) {
180   CEF_REQUIRE_UI_THREAD();
181   if (!browser_view_)
182     return;
183 
184   CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
185   if (browser) {
186     // This will result in a call to CefWindow::Close() which will then call
187     // ViewsWindow::CanClose().
188     browser->GetHost()->CloseBrowser(force);
189   }
190 }
191 
SetAddress(const std::string & url)192 void ViewsWindow::SetAddress(const std::string& url) {
193   CEF_REQUIRE_UI_THREAD();
194   if (!window_)
195     return;
196 
197   // |location_bar_| may instead be a Chrome toolbar.
198   if (location_bar_ && location_bar_->AsTextfield())
199     location_bar_->AsTextfield()->SetText(url);
200 }
201 
SetTitle(const std::string & title)202 void ViewsWindow::SetTitle(const std::string& title) {
203   CEF_REQUIRE_UI_THREAD();
204   if (window_)
205     window_->SetTitle(title);
206 }
207 
SetFavicon(CefRefPtr<CefImage> image)208 void ViewsWindow::SetFavicon(CefRefPtr<CefImage> image) {
209   CEF_REQUIRE_UI_THREAD();
210 
211   // Window icons should be 16 DIP in size.
212   DCHECK_EQ(std::max(image->GetWidth(), image->GetHeight()), 16U);
213 
214   if (window_)
215     window_->SetWindowIcon(image);
216 }
217 
SetFullscreen(bool fullscreen)218 void ViewsWindow::SetFullscreen(bool fullscreen) {
219   CEF_REQUIRE_UI_THREAD();
220   if (window_) {
221     // Hide the top controls while in full-screen mode.
222     if (with_controls_)
223       ShowTopControls(!fullscreen);
224 
225     window_->SetFullscreen(fullscreen);
226   }
227 }
228 
SetAlwaysOnTop(bool on_top)229 void ViewsWindow::SetAlwaysOnTop(bool on_top) {
230   CEF_REQUIRE_UI_THREAD();
231   if (window_) {
232     window_->SetAlwaysOnTop(on_top);
233   }
234 }
235 
SetLoadingState(bool isLoading,bool canGoBack,bool canGoForward)236 void ViewsWindow::SetLoadingState(bool isLoading,
237                                   bool canGoBack,
238                                   bool canGoForward) {
239   CEF_REQUIRE_UI_THREAD();
240   if (!window_ || chrome_toolbar_type_ == CEF_CTT_NORMAL)
241     return;
242 
243   if (with_controls_) {
244     EnableView(ID_BACK_BUTTON, canGoBack);
245     EnableView(ID_FORWARD_BUTTON, canGoForward);
246     EnableView(ID_RELOAD_BUTTON, !isLoading);
247     EnableView(ID_STOP_BUTTON, isLoading);
248   }
249   if (location_bar_) {
250     EnableView(ID_URL_TEXTFIELD, true);
251   }
252 }
253 
SetDraggableRegions(const std::vector<CefDraggableRegion> & regions)254 void ViewsWindow::SetDraggableRegions(
255     const std::vector<CefDraggableRegion>& regions) {
256   CEF_REQUIRE_UI_THREAD();
257 
258   if (!window_ || !browser_view_)
259     return;
260 
261   std::vector<CefDraggableRegion> window_regions;
262 
263   // Convert the regions from BrowserView to Window coordinates.
264   std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
265   for (; it != regions.end(); ++it) {
266     CefDraggableRegion region = *it;
267     CefPoint origin = CefPoint(region.bounds.x, region.bounds.y);
268     browser_view_->ConvertPointToWindow(origin);
269     region.bounds.x = origin.x;
270     region.bounds.y = origin.y;
271     window_regions.push_back(region);
272   }
273 
274   if (overlay_controls_) {
275     // Exclude all regions obscured by overlays.
276     overlay_controls_->UpdateDraggableRegions(window_regions);
277   }
278 
279   window_->SetDraggableRegions(window_regions);
280 }
281 
TakeFocus(bool next)282 void ViewsWindow::TakeFocus(bool next) {
283   CEF_REQUIRE_UI_THREAD();
284 
285   if (!window_)
286     return;
287 
288   if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
289     top_toolbar_->RequestFocus();
290   } else if (location_bar_) {
291     // Give focus to the location bar.
292     location_bar_->RequestFocus();
293   }
294 }
295 
OnBeforeContextMenu(CefRefPtr<CefMenuModel> model)296 void ViewsWindow::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
297   CEF_REQUIRE_UI_THREAD();
298 
299   views_style::ApplyTo(model);
300 }
301 
OnExtensionsChanged(const ExtensionSet & extensions)302 void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
303   CEF_REQUIRE_UI_THREAD();
304 
305   if (extensions.empty()) {
306     if (!extensions_.empty()) {
307       extensions_.clear();
308       UpdateExtensionControls();
309     }
310     return;
311   }
312 
313   ImageCache::ImageInfoSet image_set;
314 
315   ExtensionSet::const_iterator it = extensions.begin();
316   for (; it != extensions.end(); ++it) {
317     CefRefPtr<CefExtension> extension = *it;
318     bool internal = false;
319     const std::string& icon_path =
320         extension_util::GetExtensionIconPath(extension, &internal);
321     if (!icon_path.empty()) {
322       // Load the extension icon.
323       image_set.push_back(
324           ImageCache::ImageInfo::Create1x(icon_path, icon_path, internal));
325     } else {
326       // Get a nullptr image and use the default icon.
327       image_set.push_back(ImageCache::ImageInfo::Empty());
328     }
329   }
330 
331   delegate_->GetImageCache()->LoadImages(
332       image_set,
333       base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
334 }
335 
GetDelegateForPopupBrowserView(CefRefPtr<CefBrowserView> browser_view,const CefBrowserSettings & settings,CefRefPtr<CefClient> client,bool is_devtools)336 CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
337     CefRefPtr<CefBrowserView> browser_view,
338     const CefBrowserSettings& settings,
339     CefRefPtr<CefClient> client,
340     bool is_devtools) {
341   CEF_REQUIRE_UI_THREAD();
342 
343   // The popup browser client is created in CefLifeSpanHandler::OnBeforePopup()
344   // (e.g. via RootWindowViews::InitAsPopup()). The Delegate (RootWindowViews)
345   // knows the association between |client| and itself.
346   Delegate* popup_delegate = delegate_->GetDelegateForPopup(client);
347 
348   // Should not be the same RootWindowViews that owns |this|.
349   DCHECK(popup_delegate && popup_delegate != delegate_);
350 
351   // Create a new ViewsWindow for the popup BrowserView.
352   return new ViewsWindow(popup_delegate, nullptr);
353 }
354 
OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,CefRefPtr<CefBrowserView> popup_browser_view,bool is_devtools)355 bool ViewsWindow::OnPopupBrowserViewCreated(
356     CefRefPtr<CefBrowserView> browser_view,
357     CefRefPtr<CefBrowserView> popup_browser_view,
358     bool is_devtools) {
359   CEF_REQUIRE_UI_THREAD();
360 
361   // Retrieve the ViewsWindow created in GetDelegateForPopupBrowserView.
362   CefRefPtr<ViewsWindow> popup_window =
363       static_cast<ViewsWindow*>(static_cast<CefBrowserViewDelegate*>(
364           popup_browser_view->GetDelegate().get()));
365 
366   // Should not be the same ViewsWindow as |this|.
367   DCHECK(popup_window && popup_window != this);
368 
369   // Associate the ViewsWindow with the new popup browser.
370   popup_window->SetBrowserView(popup_browser_view);
371 
372   // Create a new top-level Window for the popup. It will show itself after
373   // creation.
374   CefWindow::CreateTopLevelWindow(popup_window);
375 
376   // We created the Window.
377   return true;
378 }
379 
GetChromeToolbarType()380 CefBrowserViewDelegate::ChromeToolbarType ViewsWindow::GetChromeToolbarType() {
381   return chrome_toolbar_type_;
382 }
383 
OnButtonPressed(CefRefPtr<CefButton> button)384 void ViewsWindow::OnButtonPressed(CefRefPtr<CefButton> button) {
385   CEF_REQUIRE_UI_THREAD();
386   DCHECK(with_controls_);
387 
388   if (!browser_view_)
389     return;
390 
391   CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
392   if (!browser)
393     return;
394 
395   switch (button->GetID()) {
396     case ID_BACK_BUTTON:
397       browser->GoBack();
398       break;
399     case ID_FORWARD_BUTTON:
400       browser->GoForward();
401       break;
402     case ID_STOP_BUTTON:
403       browser->StopLoad();
404       break;
405     case ID_RELOAD_BUTTON:
406       browser->Reload();
407       break;
408     case ID_MENU_BUTTON:
409       break;
410     default:
411       NOTREACHED();
412       break;
413   }
414 }
415 
OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,const CefPoint & screen_point,CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock)416 void ViewsWindow::OnMenuButtonPressed(
417     CefRefPtr<CefMenuButton> menu_button,
418     const CefPoint& screen_point,
419     CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
420   CEF_REQUIRE_UI_THREAD();
421 
422   const int id = menu_button->GetID();
423   if (id >= ID_EXTENSION_BUTTON_FIRST && id <= ID_EXTENSION_BUTTON_LAST) {
424     const size_t extension_idx = id - ID_EXTENSION_BUTTON_FIRST;
425     if (extension_idx >= extensions_.size()) {
426       LOG(ERROR) << "Invalid extension index " << extension_idx;
427       return;
428     }
429 
430     // Keep the button pressed until the extension window is closed.
431     extension_button_pressed_lock_ = button_pressed_lock;
432 
433     // Create a window for the extension.
434     delegate_->CreateExtensionWindow(
435         extensions_[extension_idx].extension_, menu_button->GetBoundsInScreen(),
436         window_, base::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
437     return;
438   }
439 
440   DCHECK(with_controls_ || with_overlay_controls_);
441   DCHECK_EQ(ID_MENU_BUTTON, id);
442 
443   auto point = screen_point;
444   if (with_overlay_controls_) {
445     // Align the menu correctly under the button.
446     const int button_width = menu_button->GetSize().width;
447     if (CefIsRTL()) {
448       point.x += button_width - 4;
449     } else {
450       point.x -= button_width - 4;
451     }
452   }
453 
454   menu_button->ShowMenu(button_menu_model_, point,
455                         with_overlay_controls_ ? CEF_MENU_ANCHOR_TOPLEFT
456                                                : CEF_MENU_ANCHOR_TOPRIGHT);
457 }
458 
ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,int command_id,cef_event_flags_t event_flags)459 void ViewsWindow::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
460                                  int command_id,
461                                  cef_event_flags_t event_flags) {
462   CEF_REQUIRE_UI_THREAD();
463   DCHECK(with_controls_ || with_overlay_controls_);
464 
465   if (command_id == ID_QUIT) {
466     delegate_->OnExit();
467   } else if (command_id >= ID_TESTS_FIRST && command_id <= ID_TESTS_LAST) {
468     delegate_->OnTest(command_id);
469   } else {
470     NOTREACHED();
471   }
472 }
473 
OnKeyEvent(CefRefPtr<CefTextfield> textfield,const CefKeyEvent & event)474 bool ViewsWindow::OnKeyEvent(CefRefPtr<CefTextfield> textfield,
475                              const CefKeyEvent& event) {
476   CEF_REQUIRE_UI_THREAD();
477   DCHECK_EQ(ID_URL_TEXTFIELD, textfield->GetID());
478 
479   // Trigger when the return key is pressed.
480   if (window_ && browser_view_ && event.type == KEYEVENT_RAWKEYDOWN &&
481       event.windows_key_code == VK_RETURN) {
482     CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
483     if (browser) {
484       const CefString& url = textfield->GetText();
485       if (!url.empty())
486         browser->GetMainFrame()->LoadURL(url);
487     }
488 
489     // We handled the event.
490     return true;
491   }
492 
493   return false;
494 }
495 
OnWindowCreated(CefRefPtr<CefWindow> window)496 void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
497   CEF_REQUIRE_UI_THREAD();
498   DCHECK(browser_view_);
499   DCHECK(!window_);
500   DCHECK(window);
501 
502   window_ = window;
503   window_->SetID(ID_WINDOW);
504 
505   with_controls_ = delegate_->WithControls();
506 
507   delegate_->OnViewsWindowCreated(this);
508 
509   const CefRect bounds = GetInitialBounds();
510   if (bounds.x == 0 && bounds.y == 0) {
511     // Size the Window and center it.
512     window_->CenterWindow(CefSize(bounds.width, bounds.height));
513   } else {
514     // Set the Window bounds as specified.
515     window_->SetBounds(bounds);
516   }
517 
518   // Set the background color for regions that are not obscured by other Views.
519   views_style::ApplyTo(window_.get());
520 
521   if (with_controls_ || with_overlay_controls_) {
522     // Create the MenuModel that will be displayed via the menu button.
523     CreateMenuModel();
524   }
525 
526   if (with_controls_) {
527     // Add the BrowserView and other controls to the Window.
528     AddBrowserView();
529 
530     // Add keyboard accelerators to the Window.
531     AddAccelerators();
532 
533     // Hide the top controls while in full-screen mode.
534     if (initial_show_state_ == CEF_SHOW_STATE_FULLSCREEN) {
535       ShowTopControls(false);
536     }
537   } else {
538     // Add the BrowserView as the only child of the Window.
539     window_->AddChildView(browser_view_);
540 
541     if (!delegate_->WithExtension()) {
542       // Choose a reasonable minimum window size.
543       minimum_window_size_ = CefSize(100, 100);
544     }
545   }
546 
547   if (!delegate_->InitiallyHidden()) {
548     // Show the Window.
549     Show();
550   }
551 }
552 
OnWindowDestroyed(CefRefPtr<CefWindow> window)553 void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
554   CEF_REQUIRE_UI_THREAD();
555   DCHECK(window_);
556 
557   delegate_->OnViewsWindowDestroyed(this);
558 
559   browser_view_ = nullptr;
560   button_menu_model_ = nullptr;
561   if (top_menu_bar_) {
562     top_menu_bar_->Reset();
563     top_menu_bar_ = nullptr;
564   }
565   extensions_panel_ = nullptr;
566   menu_button_ = nullptr;
567   window_ = nullptr;
568 }
569 
CanClose(CefRefPtr<CefWindow> window)570 bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
571   CEF_REQUIRE_UI_THREAD();
572 
573   // Allow the window to close if the browser says it's OK.
574   CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
575   if (browser)
576     return browser->GetHost()->TryCloseBrowser();
577   return true;
578 }
579 
GetParentWindow(CefRefPtr<CefWindow> window,bool * is_menu,bool * can_activate_menu)580 CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window,
581                                                   bool* is_menu,
582                                                   bool* can_activate_menu) {
583   CEF_REQUIRE_UI_THREAD();
584   CefRefPtr<CefWindow> parent_window = delegate_->GetParentWindow();
585   if (parent_window) {
586     // Should be an extension window, in which case we want it to behave as a
587     // menu and allow activation.
588     DCHECK(delegate_->WithExtension());
589     *is_menu = true;
590     *can_activate_menu = true;
591   }
592   return parent_window;
593 }
594 
GetInitialBounds(CefRefPtr<CefWindow> window)595 CefRect ViewsWindow::GetInitialBounds(CefRefPtr<CefWindow> window) {
596   CEF_REQUIRE_UI_THREAD();
597   if (frameless_) {
598     // Need to provide a size for frameless windows that will be centered.
599     const CefRect bounds = GetInitialBounds();
600     if (bounds.x == 0 && bounds.y == 0) {
601       return bounds;
602     }
603   }
604   return CefRect();
605 }
606 
GetInitialShowState(CefRefPtr<CefWindow> window)607 cef_show_state_t ViewsWindow::GetInitialShowState(CefRefPtr<CefWindow> window) {
608   CEF_REQUIRE_UI_THREAD();
609   return initial_show_state_;
610 }
611 
IsFrameless(CefRefPtr<CefWindow> window)612 bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
613   CEF_REQUIRE_UI_THREAD();
614   return frameless_;
615 }
616 
CanResize(CefRefPtr<CefWindow> window)617 bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) {
618   CEF_REQUIRE_UI_THREAD();
619   // Don't allow windows hosting extensions to resize.
620   return !delegate_->WithExtension();
621 }
622 
OnAccelerator(CefRefPtr<CefWindow> window,int command_id)623 bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
624   CEF_REQUIRE_UI_THREAD();
625 
626   if (command_id == ID_QUIT) {
627     delegate_->OnExit();
628     return true;
629   }
630 
631   return false;
632 }
633 
OnKeyEvent(CefRefPtr<CefWindow> window,const CefKeyEvent & event)634 bool ViewsWindow::OnKeyEvent(CefRefPtr<CefWindow> window,
635                              const CefKeyEvent& event) {
636   CEF_REQUIRE_UI_THREAD();
637 
638   if (!window_)
639     return false;
640 
641   if (delegate_->WithExtension() && event.type == KEYEVENT_RAWKEYDOWN &&
642       event.windows_key_code == VK_ESCAPE) {
643     // Close the extension window on escape.
644     Close(false);
645     return true;
646   }
647 
648   if (!with_controls_)
649     return false;
650 
651   if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) {
652     // ALT key is pressed.
653     int last_focused_view = last_focused_view_;
654     bool menu_had_focus = menu_has_focus_;
655 
656     // Toggle menu button focusable.
657     SetMenuFocusable(!menu_has_focus_);
658 
659     if (menu_had_focus && last_focused_view != 0) {
660       // Restore focus to the view that was previously focused.
661       window_->GetViewForID(last_focused_view)->RequestFocus();
662     }
663 
664     return true;
665   }
666 
667   if (menu_has_focus_ && top_menu_bar_)
668     return top_menu_bar_->OnKeyEvent(event);
669 
670   return false;
671 }
672 
GetMinimumSize(CefRefPtr<CefView> view)673 CefSize ViewsWindow::GetMinimumSize(CefRefPtr<CefView> view) {
674   CEF_REQUIRE_UI_THREAD();
675 
676   if (view->GetID() == ID_WINDOW)
677     return minimum_window_size_;
678 
679   return CefSize();
680 }
681 
OnFocus(CefRefPtr<CefView> view)682 void ViewsWindow::OnFocus(CefRefPtr<CefView> view) {
683   CEF_REQUIRE_UI_THREAD();
684 
685   const int view_id = view->GetID();
686 
687   // Keep track of the non-menu view that was last focused.
688   if (last_focused_view_ != view_id &&
689       (!top_menu_bar_ || !top_menu_bar_->HasMenuId(view_id))) {
690     last_focused_view_ = view_id;
691   }
692 
693   // When focus leaves the menu buttons make them unfocusable.
694   if (menu_has_focus_) {
695     if (top_menu_bar_) {
696       if (!top_menu_bar_->HasMenuId(view_id))
697         SetMenuFocusable(false);
698     } else if (view_id != ID_MENU_BUTTON) {
699       SetMenuFocusable(false);
700     }
701   }
702 
703   if (view_id == ID_BROWSER_VIEW)
704     delegate_->OnViewsWindowActivated(this);
705 }
706 
OnBlur(CefRefPtr<CefView> view)707 void ViewsWindow::OnBlur(CefRefPtr<CefView> view) {
708   CEF_REQUIRE_UI_THREAD();
709 
710   const int view_id = view->GetID();
711   if (view_id == ID_BROWSER_VIEW && delegate_->WithExtension()) {
712     // Close windows hosting extensions when the browser loses focus.
713     Close(false);
714   }
715 }
716 
OnWindowChanged(CefRefPtr<CefView> view,bool added)717 void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
718   const int view_id = view->GetID();
719   if (view_id != ID_BROWSER_VIEW)
720     return;
721 
722   if (added) {
723     if (with_controls_) {
724       AddControls();
725     }
726 
727     if (with_overlay_controls_) {
728       overlay_controls_ = new ViewsOverlayControls();
729       overlay_controls_->Initialize(window_, CreateMenuButton(),
730                                     CreateLocationBar(),
731                                     chrome_toolbar_type_ != CEF_CTT_NONE);
732     }
733   } else {
734     if (overlay_controls_) {
735       // Overlay controls may include the Chrome toolbar, in which case they
736       // need to be removed before the BrowserView.
737       overlay_controls_->Destroy();
738       overlay_controls_ = nullptr;
739       location_bar_ = nullptr;
740     }
741   }
742 }
743 
OnLayoutChanged(CefRefPtr<CefView> view,const CefRect & new_bounds)744 void ViewsWindow::OnLayoutChanged(CefRefPtr<CefView> view,
745                                   const CefRect& new_bounds) {
746   const int view_id = view->GetID();
747   if (view_id != ID_BROWSER_VIEW)
748     return;
749 
750   if (overlay_controls_) {
751     overlay_controls_->UpdateControls();
752   }
753 }
754 
MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,int command_id,cef_event_flags_t event_flags)755 void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
756                                         int command_id,
757                                         cef_event_flags_t event_flags) {
758   ExecuteCommand(menu_model, command_id, event_flags);
759 }
760 
ViewsWindow(Delegate * delegate,CefRefPtr<CefBrowserView> browser_view)761 ViewsWindow::ViewsWindow(Delegate* delegate,
762                          CefRefPtr<CefBrowserView> browser_view)
763     : delegate_(delegate),
764       with_controls_(false),
765       menu_has_focus_(false),
766       last_focused_view_(false) {
767   DCHECK(delegate_);
768   if (browser_view)
769     SetBrowserView(browser_view);
770 
771   CefRefPtr<CefCommandLine> command_line =
772       CefCommandLine::GetGlobalCommandLine();
773 
774   const bool hide_frame = command_line->HasSwitch(switches::kHideFrame);
775   const bool hide_overlays = command_line->HasSwitch(switches::kHideOverlays);
776 
777   // Without a window frame.
778   frameless_ = hide_frame || delegate_->WithExtension();
779 
780   // With an overlay that mimics window controls.
781   with_overlay_controls_ =
782       hide_frame && !hide_overlays && !delegate_->WithControls();
783 
784   if (MainContext::Get()->UseChromeRuntime()) {
785     const std::string& toolbar_type =
786         command_line->GetSwitchValue(switches::kShowChromeToolbar);
787     if (toolbar_type == "none") {
788       chrome_toolbar_type_ = CEF_CTT_NONE;
789     } else if (toolbar_type == "location") {
790       chrome_toolbar_type_ = CEF_CTT_LOCATION;
791     } else {
792       chrome_toolbar_type_ =
793           with_overlay_controls_ ? CEF_CTT_LOCATION : CEF_CTT_NORMAL;
794     }
795   } else {
796     chrome_toolbar_type_ = CEF_CTT_NONE;
797   }
798 
799   const std::string& show_state =
800       command_line->GetSwitchValue(switches::kInitialShowState);
801   if (show_state == "minimized") {
802     initial_show_state_ = CEF_SHOW_STATE_MINIMIZED;
803   } else if (show_state == "maximized") {
804     initial_show_state_ = CEF_SHOW_STATE_MAXIMIZED;
805   } else if (show_state == "fullscreen") {
806     initial_show_state_ = CEF_SHOW_STATE_FULLSCREEN;
807   }
808 
809 #if !defined(OS_MAC)
810   // On Mac we don't show a top menu on the window. The options are available in
811   // the app menu instead.
812   if (!command_line->HasSwitch(switches::kHideTopMenu)) {
813     top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST);
814   }
815 #endif
816 }
817 
SetBrowserView(CefRefPtr<CefBrowserView> browser_view)818 void ViewsWindow::SetBrowserView(CefRefPtr<CefBrowserView> browser_view) {
819   DCHECK(!browser_view_);
820   DCHECK(browser_view);
821   DCHECK(browser_view->IsValid());
822   DCHECK(!browser_view->IsAttached());
823   browser_view_ = browser_view;
824   browser_view_->SetID(ID_BROWSER_VIEW);
825 }
826 
CreateMenuModel()827 void ViewsWindow::CreateMenuModel() {
828   // Create the menu button model.
829   button_menu_model_ = CefMenuModel::CreateMenuModel(this);
830   CefRefPtr<CefMenuModel> test_menu =
831       button_menu_model_->AddSubMenu(0, "&Tests");
832   views_style::ApplyTo(button_menu_model_);
833   AddTestMenuItems(test_menu);
834   AddFileMenuItems(button_menu_model_);
835 
836   if (top_menu_bar_) {
837     // Add the menus to the top menu bar.
838     AddFileMenuItems(top_menu_bar_->CreateMenuModel("&File", nullptr));
839     AddTestMenuItems(top_menu_bar_->CreateMenuModel("&Tests", nullptr));
840   }
841 }
842 
CreateBrowseButton(const std::string & label,int id)843 CefRefPtr<CefLabelButton> ViewsWindow::CreateBrowseButton(
844     const std::string& label,
845     int id) {
846   CefRefPtr<CefLabelButton> button =
847       CefLabelButton::CreateLabelButton(this, label);
848   button->SetID(id);
849   views_style::ApplyTo(button.get());
850   button->SetInkDropEnabled(true);
851   button->SetEnabled(false);    // Disabled by default.
852   button->SetFocusable(false);  // Don't give focus to the button.
853 
854   return button;
855 }
856 
CreateMenuButton()857 CefRefPtr<CefMenuButton> ViewsWindow::CreateMenuButton() {
858   // Create the menu button.
859   DCHECK(!menu_button_);
860   menu_button_ = CefMenuButton::CreateMenuButton(this, CefString());
861   menu_button_->SetID(ID_MENU_BUTTON);
862   menu_button_->SetImage(
863       CEF_BUTTON_STATE_NORMAL,
864       delegate_->GetImageCache()->GetCachedImage("menu_icon"));
865   views_style::ApplyTo(menu_button_.get());
866   menu_button_->SetInkDropEnabled(true);
867   // Override the default minimum size.
868   menu_button_->SetMinimumSize(CefSize(0, 0));
869   return menu_button_;
870 }
871 
CreateLocationBar()872 CefRefPtr<CefView> ViewsWindow::CreateLocationBar() {
873   DCHECK(!location_bar_);
874   if (chrome_toolbar_type_ == CEF_CTT_LOCATION) {
875     // Chrome will provide a minimal location bar.
876     location_bar_ = browser_view_->GetChromeToolbar();
877     DCHECK(location_bar_);
878     views_style::ApplyBackgroundTo(location_bar_);
879   }
880   if (!location_bar_) {
881     // Create the URL textfield.
882     CefRefPtr<CefTextfield> url_textfield = CefTextfield::CreateTextfield(this);
883     url_textfield->SetID(ID_URL_TEXTFIELD);
884     url_textfield->SetEnabled(false);  // Disabled by default.
885     views_style::ApplyTo(url_textfield);
886     location_bar_ = url_textfield;
887   }
888   return location_bar_;
889 }
890 
AddBrowserView()891 void ViewsWindow::AddBrowserView() {
892   // Use a vertical box layout for |window|.
893   CefBoxLayoutSettings window_layout_settings;
894   window_layout_settings.horizontal = false;
895   window_layout_settings.between_child_spacing = 2;
896   CefRefPtr<CefBoxLayout> window_layout =
897       window_->SetToBoxLayout(window_layout_settings);
898 
899   window_->AddChildView(browser_view_);
900 
901   // Allow |browser_view_| to grow and fill any remaining space.
902   window_layout->SetFlexForView(browser_view_, 1);
903 
904   // Remaining setup will be performed in OnWindowChanged after the BrowserView
905   // is added to the CefWindow. This is necessary because Chrome toolbars are
906   // only available after the BrowserView is added.
907 }
908 
AddControls()909 void ViewsWindow::AddControls() {
910   // Build the remainder of the UI now that the BrowserView has been added to
911   // the CefWindow. This is a requirement to use Chrome toolbars.
912 
913   CefRefPtr<CefPanel> top_menu_panel;
914   if (top_menu_bar_)
915     top_menu_panel = top_menu_bar_->GetMenuPanel();
916 
917   LabelButtons browse_buttons;
918 
919   if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
920     // Chrome will provide a normal toolbar with location, menu, etc.
921     top_toolbar_ = browser_view_->GetChromeToolbar();
922     DCHECK(top_toolbar_);
923   }
924 
925   if (!top_toolbar_) {
926     // Create the browse buttons.
927     browse_buttons.push_back(CreateBrowseButton("Back", ID_BACK_BUTTON));
928     browse_buttons.push_back(CreateBrowseButton("Forward", ID_FORWARD_BUTTON));
929     browse_buttons.push_back(CreateBrowseButton("Reload", ID_RELOAD_BUTTON));
930     browse_buttons.push_back(CreateBrowseButton("Stop", ID_STOP_BUTTON));
931 
932     CreateLocationBar();
933     CreateMenuButton();
934 
935     // Create the top panel.
936     CefRefPtr<CefPanel> top_panel = CefPanel::CreatePanel(nullptr);
937 
938     // Use a horizontal box layout for |top_panel|.
939     CefBoxLayoutSettings top_panel_layout_settings;
940     top_panel_layout_settings.horizontal = true;
941     CefRefPtr<CefBoxLayout> top_panel_layout =
942         top_panel->SetToBoxLayout(top_panel_layout_settings);
943 
944     // Add the buttons and URL textfield to |top_panel|.
945     for (size_t i = 0U; i < browse_buttons.size(); ++i)
946       top_panel->AddChildView(browse_buttons[i]);
947     top_panel->AddChildView(location_bar_);
948 
949     UpdateExtensionControls();
950     DCHECK(extensions_panel_);
951     top_panel->AddChildView(extensions_panel_);
952 
953     top_panel->AddChildView(menu_button_);
954     views_style::ApplyTo(top_panel);
955 
956     // Allow |location| to grow and fill any remaining space.
957     top_panel_layout->SetFlexForView(location_bar_, 1);
958 
959     top_toolbar_ = top_panel;
960   }
961 
962   // Add the top panel and browser view to |window|.
963   int top_index = 0;
964   if (top_menu_panel)
965     window_->AddChildViewAt(top_menu_panel, top_index++);
966   window_->AddChildViewAt(top_toolbar_, top_index);
967 
968   // Lay out |window| so we can get the default button sizes.
969   window_->Layout();
970 
971   int min_width = 200;
972   if (!browse_buttons.empty()) {
973     // Make all browse buttons the same size.
974     MakeButtonsSameSize(browse_buttons);
975 
976     // Lay out |window| again with the new button sizes.
977     window_->Layout();
978 
979     // Minimum window width is the size of all buttons plus some extra.
980     min_width = browse_buttons[0]->GetBounds().width * 4 +
981                 menu_button_->GetBounds().width + 100;
982   }
983 
984   // Minimum window height is the hight of the top toolbar plus some extra.
985   int min_height = top_toolbar_->GetBounds().height + 100;
986   if (top_menu_panel)
987     min_height += top_menu_panel->GetBounds().height;
988 
989   minimum_window_size_ = CefSize(min_width, min_height);
990 }
991 
AddAccelerators()992 void ViewsWindow::AddAccelerators() {
993   // Trigger accelerators without first forwarding to web content.
994   browser_view_->SetPreferAccelerators(true);
995 
996   // Specify the accelerators to handle. OnAccelerator will be called when the
997   // accelerator is triggered.
998   window_->SetAccelerator(ID_QUIT, 'X', false, false, true);
999 }
1000 
SetMenuFocusable(bool focusable)1001 void ViewsWindow::SetMenuFocusable(bool focusable) {
1002   if (!window_ || !with_controls_)
1003     return;
1004 
1005   if (top_menu_bar_) {
1006     top_menu_bar_->SetMenuFocusable(focusable);
1007   } else {
1008     window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable);
1009 
1010     if (focusable) {
1011       // Give focus to menu button.
1012       window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus();
1013     }
1014   }
1015 
1016   menu_has_focus_ = focusable;
1017 }
1018 
EnableView(int id,bool enable)1019 void ViewsWindow::EnableView(int id, bool enable) {
1020   if (!window_)
1021     return;
1022   // Special handling for |location_bar_| which may be an overlay (e.g. not a
1023   // child of this view).
1024   CefRefPtr<CefView> view =
1025       id == ID_URL_TEXTFIELD ? location_bar_ : window_->GetViewForID(id);
1026   if (view)
1027     view->SetEnabled(enable);
1028 }
1029 
ShowTopControls(bool show)1030 void ViewsWindow::ShowTopControls(bool show) {
1031   if (!window_ || !with_controls_)
1032     return;
1033 
1034   // Change the visibility of the top toolbar.
1035   if (top_toolbar_->IsVisible() != show) {
1036     top_toolbar_->SetVisible(show);
1037     top_toolbar_->InvalidateLayout();
1038   }
1039 }
1040 
UpdateExtensionControls()1041 void ViewsWindow::UpdateExtensionControls() {
1042   CEF_REQUIRE_UI_THREAD();
1043 
1044   if (!window_ || !with_controls_)
1045     return;
1046 
1047   if (!extensions_panel_) {
1048     extensions_panel_ = CefPanel::CreatePanel(nullptr);
1049 
1050     // Use a horizontal box layout for |top_panel|.
1051     CefBoxLayoutSettings top_panel_layout_settings;
1052     top_panel_layout_settings.horizontal = true;
1053     CefRefPtr<CefBoxLayout> top_panel_layout =
1054         extensions_panel_->SetToBoxLayout(top_panel_layout_settings);
1055   } else {
1056     extensions_panel_->RemoveAllChildViews();
1057   }
1058 
1059   if (extensions_.size() >
1060       ID_EXTENSION_BUTTON_LAST - ID_EXTENSION_BUTTON_FIRST) {
1061     LOG(WARNING) << "Too many extensions loaded. Some will be ignored.";
1062   }
1063 
1064   ExtensionInfoSet::const_iterator it = extensions_.begin();
1065   for (int id = ID_EXTENSION_BUTTON_FIRST;
1066        it != extensions_.end() && id <= ID_EXTENSION_BUTTON_LAST; ++id, ++it) {
1067     CefRefPtr<CefMenuButton> button =
1068         CefMenuButton::CreateMenuButton(this, CefString());
1069     button->SetID(id);
1070     button->SetImage(CEF_BUTTON_STATE_NORMAL, (*it).image_);
1071     views_style::ApplyTo(button.get());
1072     button->SetInkDropEnabled(true);
1073     // Override the default minimum size.
1074     button->SetMinimumSize(CefSize(0, 0));
1075 
1076     extensions_panel_->AddChildView(button);
1077   }
1078 
1079   CefRefPtr<CefView> parent_view = extensions_panel_->GetParentView();
1080   if (parent_view)
1081     parent_view->InvalidateLayout();
1082 }
1083 
OnExtensionIconsLoaded(const ExtensionSet & extensions,const ImageCache::ImageSet & images)1084 void ViewsWindow::OnExtensionIconsLoaded(const ExtensionSet& extensions,
1085                                          const ImageCache::ImageSet& images) {
1086   if (!CefCurrentlyOn(TID_UI)) {
1087     // Execute this method on the UI thread.
1088     CefPostTask(TID_UI, base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded,
1089                                        this, extensions, images));
1090     return;
1091   }
1092 
1093   DCHECK_EQ(extensions.size(), images.size());
1094 
1095   extensions_.clear();
1096 
1097   ExtensionSet::const_iterator it1 = extensions.begin();
1098   ImageCache::ImageSet::const_iterator it2 = images.begin();
1099   for (; it1 != extensions.end() && it2 != images.end(); ++it1, ++it2) {
1100     CefRefPtr<CefImage> icon = *it2;
1101     if (!icon)
1102       icon = delegate_->GetImageCache()->GetCachedImage(kDefaultExtensionIcon);
1103     extensions_.push_back(ExtensionInfo(*it1, icon));
1104   }
1105 
1106   UpdateExtensionControls();
1107 }
1108 
OnExtensionWindowClosed()1109 void ViewsWindow::OnExtensionWindowClosed() {
1110   if (!CefCurrentlyOn(TID_UI)) {
1111     // Execute this method on the UI thread.
1112     CefPostTask(TID_UI,
1113                 base::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
1114     return;
1115   }
1116 
1117   // Restore the button state.
1118   extension_button_pressed_lock_ = nullptr;
1119 }
1120 
GetInitialBounds() const1121 CefRect ViewsWindow::GetInitialBounds() const {
1122   CEF_REQUIRE_UI_THREAD();
1123   CefRect bounds = delegate_->GetWindowBounds();
1124   if (bounds.IsEmpty()) {
1125     // Use the default size.
1126     bounds.width = 800;
1127     bounds.height = 600;
1128   }
1129   return bounds;
1130 }
1131 
1132 }  // namespace client
1133