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