| // Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights |
| // reserved. Use of this source code is governed by a BSD-style license that |
| // can be found in the LICENSE file. |
| |
| #include "tests/cefclient/browser/views_window.h" |
| |
| #include <algorithm> |
| |
| #include "include/base/cef_bind.h" |
| #include "include/base/cef_build.h" |
| #include "include/cef_app.h" |
| #include "include/views/cef_box_layout.h" |
| #include "include/wrapper/cef_helpers.h" |
| #include "tests/cefclient/browser/resource.h" |
| #include "tests/cefclient/browser/views_style.h" |
| #include "tests/shared/browser/extension_util.h" |
| #include "tests/shared/common/client_switches.h" |
| |
| #if !defined(OS_WIN) |
| #define VK_ESCAPE 0x1B |
| #define VK_RETURN 0x0D |
| #define VK_MENU 0x12 // ALT key. |
| #endif |
| |
| namespace client { |
| |
| namespace { |
| |
| const char kDefaultExtensionIcon[] = "window_icon"; |
| |
| // Control IDs for Views in the top-level Window. |
| enum ControlIds { |
| ID_WINDOW = 1, |
| ID_BROWSER_VIEW, |
| ID_BACK_BUTTON, |
| ID_FORWARD_BUTTON, |
| ID_STOP_BUTTON, |
| ID_RELOAD_BUTTON, |
| ID_URL_TEXTFIELD, |
| ID_MENU_BUTTON, |
| |
| // Reserved range of top menu button IDs. |
| ID_TOP_MENU_FIRST, |
| ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10, |
| |
| // Reserved range of extension button IDs. |
| ID_EXTENSION_BUTTON_FIRST, |
| ID_EXTENSION_BUTTON_LAST = ID_EXTENSION_BUTTON_FIRST + 10, |
| }; |
| |
| typedef std::vector<CefRefPtr<CefLabelButton>> LabelButtons; |
| |
| // Make all |buttons| the same size. |
| void MakeButtonsSameSize(const LabelButtons& buttons) { |
| CefSize size; |
| |
| // Determine the largest button size. |
| for (size_t i = 0U; i < buttons.size(); ++i) { |
| const CefSize& button_size = buttons[i]->GetPreferredSize(); |
| if (size.width < button_size.width) |
| size.width = button_size.width; |
| if (size.height < button_size.height) |
| size.height = button_size.height; |
| } |
| |
| for (size_t i = 0U; i < buttons.size(); ++i) { |
| // Set the button's minimum size. |
| buttons[i]->SetMinimumSize(size); |
| |
| // Re-layout the button and all parent Views. |
| buttons[i]->InvalidateLayout(); |
| } |
| } |
| |
| void AddTestMenuItems(CefRefPtr<CefMenuModel> test_menu) { |
| test_menu->AddItem(ID_TESTS_GETSOURCE, "Get Source"); |
| test_menu->AddItem(ID_TESTS_GETTEXT, "Get Text"); |
| test_menu->AddItem(ID_TESTS_WINDOW_NEW, "New Window"); |
| test_menu->AddItem(ID_TESTS_WINDOW_POPUP, "Popup Window"); |
| test_menu->AddItem(ID_TESTS_REQUEST, "Request"); |
| test_menu->AddItem(ID_TESTS_PLUGIN_INFO, "Plugin Info"); |
| test_menu->AddItem(ID_TESTS_ZOOM_IN, "Zoom In"); |
| test_menu->AddItem(ID_TESTS_ZOOM_OUT, "Zoom Out"); |
| test_menu->AddItem(ID_TESTS_ZOOM_RESET, "Zoom Reset"); |
| test_menu->AddItem(ID_TESTS_TRACING_BEGIN, "Begin Tracing"); |
| test_menu->AddItem(ID_TESTS_TRACING_END, "End Tracing"); |
| test_menu->AddItem(ID_TESTS_PRINT, "Print"); |
| test_menu->AddItem(ID_TESTS_PRINT_TO_PDF, "Print to PDF"); |
| test_menu->AddItem(ID_TESTS_MUTE_AUDIO, "Mute Audio"); |
| test_menu->AddItem(ID_TESTS_UNMUTE_AUDIO, "Unmute Audio"); |
| test_menu->AddItem(ID_TESTS_OTHER_TESTS, "Other Tests"); |
| } |
| |
| void AddFileMenuItems(CefRefPtr<CefMenuModel> file_menu) { |
| file_menu->AddItem(ID_QUIT, "E&xit"); |
| |
| // Show the accelerator shortcut text in the menu. |
| file_menu->SetAcceleratorAt(file_menu->GetCount() - 1, 'X', false, false, |
| true); |
| } |
| |
| } // namespace |
| |
| // static |
| CefRefPtr<ViewsWindow> ViewsWindow::Create( |
| Delegate* delegate, |
| CefRefPtr<CefClient> client, |
| const CefString& url, |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefRequestContext> request_context) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(delegate); |
| |
| // Create a new ViewsWindow. |
| CefRefPtr<ViewsWindow> views_window = new ViewsWindow(delegate, nullptr); |
| |
| // Create a new BrowserView. |
| CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView( |
| client, url, settings, nullptr, request_context, views_window); |
| |
| // Associate the BrowserView with the ViewsWindow. |
| views_window->SetBrowserView(browser_view); |
| |
| // Create a new top-level Window. It will show itself after creation. |
| CefWindow::CreateTopLevelWindow(views_window); |
| |
| return views_window; |
| } |
| |
| void ViewsWindow::Show() { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->Show(); |
| if (browser_view_) { |
| // Give keyboard focus to the BrowserView. |
| browser_view_->RequestFocus(); |
| } |
| } |
| |
| void ViewsWindow::Hide() { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->Hide(); |
| } |
| |
| void ViewsWindow::Minimize() { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->Minimize(); |
| } |
| |
| void ViewsWindow::Maximize() { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->Maximize(); |
| } |
| |
| void ViewsWindow::SetBounds(const CefRect& bounds) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->SetBounds(bounds); |
| } |
| |
| void ViewsWindow::SetBrowserSize(const CefSize& size, |
| bool has_position, |
| const CefPoint& position) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (browser_view_) |
| browser_view_->SetSize(size); |
| if (window_) { |
| window_->SizeToPreferredSize(); |
| if (has_position) |
| window_->SetPosition(position); |
| } |
| } |
| |
| void ViewsWindow::Close(bool force) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (!browser_view_) |
| return; |
| |
| CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser(); |
| if (browser) { |
| // This will result in a call to CefWindow::Close() which will then call |
| // ViewsWindow::CanClose(). |
| browser->GetHost()->CloseBrowser(force); |
| } |
| } |
| |
| void ViewsWindow::SetAddress(const std::string& url) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (!window_ || !with_controls_) |
| return; |
| |
| CefRefPtr<CefView> view = window_->GetViewForID(ID_URL_TEXTFIELD); |
| if (view && view->AsTextfield()) |
| view->AsTextfield()->SetText(url); |
| } |
| |
| void ViewsWindow::SetTitle(const std::string& title) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) |
| window_->SetTitle(title); |
| } |
| |
| void ViewsWindow::SetFavicon(CefRefPtr<CefImage> image) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Window icons should be 16 DIP in size. |
| DCHECK_EQ(std::max(image->GetWidth(), image->GetHeight()), 16U); |
| |
| if (window_) |
| window_->SetWindowIcon(image); |
| } |
| |
| void ViewsWindow::SetFullscreen(bool fullscreen) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) { |
| // Hide the top controls while in full-screen mode. |
| if (with_controls_) |
| ShowTopControls(!fullscreen); |
| |
| window_->SetFullscreen(fullscreen); |
| } |
| } |
| |
| void ViewsWindow::SetAlwaysOnTop(bool on_top) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (window_) { |
| window_->SetAlwaysOnTop(on_top); |
| } |
| } |
| |
| void ViewsWindow::SetLoadingState(bool isLoading, |
| bool canGoBack, |
| bool canGoForward) { |
| CEF_REQUIRE_UI_THREAD(); |
| if (!window_ || !with_controls_) |
| return; |
| |
| EnableView(ID_BACK_BUTTON, canGoBack); |
| EnableView(ID_FORWARD_BUTTON, canGoForward); |
| EnableView(ID_RELOAD_BUTTON, !isLoading); |
| EnableView(ID_STOP_BUTTON, isLoading); |
| EnableView(ID_URL_TEXTFIELD, true); |
| } |
| |
| void ViewsWindow::SetDraggableRegions( |
| const std::vector<CefDraggableRegion>& regions) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (!window_ || !browser_view_) |
| return; |
| |
| std::vector<CefDraggableRegion> window_regions; |
| |
| // Convert the regions from BrowserView to Window coordinates. |
| std::vector<CefDraggableRegion>::const_iterator it = regions.begin(); |
| for (; it != regions.end(); ++it) { |
| CefDraggableRegion region = *it; |
| CefPoint origin = CefPoint(region.bounds.x, region.bounds.y); |
| browser_view_->ConvertPointToWindow(origin); |
| region.bounds.x = origin.x; |
| region.bounds.y = origin.y; |
| window_regions.push_back(region); |
| } |
| |
| window_->SetDraggableRegions(window_regions); |
| } |
| |
| void ViewsWindow::TakeFocus(bool next) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (!window_ || !with_controls_) |
| return; |
| |
| // Give focus to the URL textfield. |
| window_->GetViewForID(ID_URL_TEXTFIELD)->RequestFocus(); |
| } |
| |
| void ViewsWindow::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| views_style::ApplyTo(model); |
| } |
| |
| void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (extensions.empty()) { |
| if (!extensions_.empty()) { |
| extensions_.clear(); |
| UpdateExtensionControls(); |
| } |
| return; |
| } |
| |
| ImageCache::ImageInfoSet image_set; |
| |
| ExtensionSet::const_iterator it = extensions.begin(); |
| for (; it != extensions.end(); ++it) { |
| CefRefPtr<CefExtension> extension = *it; |
| bool internal = false; |
| const std::string& icon_path = |
| extension_util::GetExtensionIconPath(extension, &internal); |
| if (!icon_path.empty()) { |
| // Load the extension icon. |
| image_set.push_back( |
| ImageCache::ImageInfo::Create1x(icon_path, icon_path, internal)); |
| } else { |
| // Get a NULL image and use the default icon. |
| image_set.push_back(ImageCache::ImageInfo::Empty()); |
| } |
| } |
| |
| delegate_->GetImageCache()->LoadImages( |
| image_set, |
| base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this, extensions)); |
| } |
| |
| CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView( |
| CefRefPtr<CefBrowserView> browser_view, |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefClient> client, |
| bool is_devtools) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // The popup browser client is created in CefLifeSpanHandler::OnBeforePopup() |
| // (e.g. via RootWindowViews::InitAsPopup()). The Delegate (RootWindowViews) |
| // knows the association between |client| and itself. |
| Delegate* popup_delegate = delegate_->GetDelegateForPopup(client); |
| |
| // Should not be the same RootWindowViews that owns |this|. |
| DCHECK(popup_delegate && popup_delegate != delegate_); |
| |
| // Create a new ViewsWindow for the popup BrowserView. |
| return new ViewsWindow(popup_delegate, nullptr); |
| } |
| |
| bool ViewsWindow::OnPopupBrowserViewCreated( |
| CefRefPtr<CefBrowserView> browser_view, |
| CefRefPtr<CefBrowserView> popup_browser_view, |
| bool is_devtools) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Retrieve the ViewsWindow created in GetDelegateForPopupBrowserView. |
| CefRefPtr<ViewsWindow> popup_window = |
| static_cast<ViewsWindow*>(static_cast<CefBrowserViewDelegate*>( |
| popup_browser_view->GetDelegate().get())); |
| |
| // Should not be the same ViewsWindow as |this|. |
| DCHECK(popup_window && popup_window != this); |
| |
| // Associate the ViewsWindow with the new popup browser. |
| popup_window->SetBrowserView(popup_browser_view); |
| |
| // Create a new top-level Window for the popup. It will show itself after |
| // creation. |
| CefWindow::CreateTopLevelWindow(popup_window); |
| |
| // We created the Window. |
| return true; |
| } |
| |
| void ViewsWindow::OnButtonPressed(CefRefPtr<CefButton> button) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(with_controls_); |
| |
| if (!browser_view_) |
| return; |
| |
| CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser(); |
| if (!browser) |
| return; |
| |
| switch (button->GetID()) { |
| case ID_BACK_BUTTON: |
| browser->GoBack(); |
| break; |
| case ID_FORWARD_BUTTON: |
| browser->GoForward(); |
| break; |
| case ID_STOP_BUTTON: |
| browser->StopLoad(); |
| break; |
| case ID_RELOAD_BUTTON: |
| browser->Reload(); |
| break; |
| case ID_MENU_BUTTON: |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void ViewsWindow::OnMenuButtonPressed( |
| CefRefPtr<CefMenuButton> menu_button, |
| const CefPoint& screen_point, |
| CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| const int id = menu_button->GetID(); |
| if (id >= ID_EXTENSION_BUTTON_FIRST && id <= ID_EXTENSION_BUTTON_LAST) { |
| const size_t extension_idx = id - ID_EXTENSION_BUTTON_FIRST; |
| if (extension_idx >= extensions_.size()) { |
| LOG(ERROR) << "Invalid extension index " << extension_idx; |
| return; |
| } |
| |
| // Keep the button pressed until the extension window is closed. |
| extension_button_pressed_lock_ = button_pressed_lock; |
| |
| // Create a window for the extension. |
| delegate_->CreateExtensionWindow( |
| extensions_[extension_idx].extension_, menu_button->GetBoundsInScreen(), |
| window_, base::Bind(&ViewsWindow::OnExtensionWindowClosed, this)); |
| return; |
| } |
| |
| DCHECK(with_controls_); |
| DCHECK_EQ(ID_MENU_BUTTON, id); |
| |
| menu_button->ShowMenu(button_menu_model_, screen_point, |
| CEF_MENU_ANCHOR_TOPRIGHT); |
| } |
| |
| void ViewsWindow::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model, |
| int command_id, |
| cef_event_flags_t event_flags) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(with_controls_); |
| |
| if (command_id == ID_QUIT) { |
| delegate_->OnExit(); |
| } else if (command_id >= ID_TESTS_FIRST && command_id <= ID_TESTS_LAST) { |
| delegate_->OnTest(command_id); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| bool ViewsWindow::OnKeyEvent(CefRefPtr<CefTextfield> textfield, |
| const CefKeyEvent& event) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(with_controls_); |
| DCHECK_EQ(ID_URL_TEXTFIELD, textfield->GetID()); |
| |
| // Trigger when the return key is pressed. |
| if (window_ && browser_view_ && event.type == KEYEVENT_RAWKEYDOWN && |
| event.windows_key_code == VK_RETURN) { |
| CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser(); |
| if (browser) { |
| CefRefPtr<CefView> view = window_->GetViewForID(ID_URL_TEXTFIELD); |
| if (view && view->AsTextfield()) { |
| const CefString& url = view->AsTextfield()->GetText(); |
| if (!url.empty()) |
| browser->GetMainFrame()->LoadURL(url); |
| } |
| } |
| |
| // We handled the event. |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(browser_view_); |
| DCHECK(!window_); |
| DCHECK(window); |
| |
| window_ = window; |
| window_->SetID(ID_WINDOW); |
| |
| with_controls_ = delegate_->WithControls(); |
| |
| delegate_->OnViewsWindowCreated(this); |
| |
| CefRect bounds = delegate_->GetWindowBounds(); |
| if (bounds.IsEmpty()) { |
| // Use the default size. |
| bounds.width = 800; |
| bounds.height = 600; |
| } |
| |
| if (bounds.x == 0 && bounds.y == 0) { |
| // Size the Window and center it. |
| window_->CenterWindow(CefSize(bounds.width, bounds.height)); |
| } else { |
| // Set the Window bounds as specified. |
| window_->SetBounds(bounds); |
| } |
| |
| // Set the background color for regions that are not obscured by other Views. |
| views_style::ApplyTo(window_.get()); |
| |
| if (with_controls_) { |
| // Add the BrowserView and other controls to the Window. |
| AddControls(); |
| |
| // Add keyboard accelerators to the Window. |
| AddAccelerators(); |
| } else { |
| // Add the BrowserView as the only child of the Window. |
| window_->AddChildView(browser_view_); |
| |
| if (!delegate_->WithExtension()) { |
| // Choose a reasonable minimum window size. |
| minimum_window_size_ = CefSize(100, 100); |
| } |
| } |
| |
| if (!delegate_->InitiallyHidden()) { |
| // Show the Window. |
| Show(); |
| } |
| } |
| |
| void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) { |
| CEF_REQUIRE_UI_THREAD(); |
| DCHECK(window_); |
| |
| delegate_->OnViewsWindowDestroyed(this); |
| |
| browser_view_ = nullptr; |
| button_menu_model_ = nullptr; |
| if (top_menu_bar_) { |
| top_menu_bar_->Reset(); |
| top_menu_bar_ = nullptr; |
| } |
| extensions_panel_ = nullptr; |
| window_ = nullptr; |
| } |
| |
| bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Allow the window to close if the browser says it's OK. |
| CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser(); |
| if (browser) |
| return browser->GetHost()->TryCloseBrowser(); |
| return true; |
| } |
| |
| CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window, |
| bool* is_menu, |
| bool* can_activate_menu) { |
| CEF_REQUIRE_UI_THREAD(); |
| CefRefPtr<CefWindow> parent_window = delegate_->GetParentWindow(); |
| if (parent_window) { |
| // Should be an extension window, in which case we want it to behave as a |
| // menu and allow activation. |
| DCHECK(delegate_->WithExtension()); |
| *is_menu = true; |
| *can_activate_menu = true; |
| } |
| return parent_window; |
| } |
| |
| bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) { |
| CEF_REQUIRE_UI_THREAD(); |
| return frameless_; |
| } |
| |
| bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) { |
| CEF_REQUIRE_UI_THREAD(); |
| // Don't allow windows hosting extensions to resize. |
| return !delegate_->WithExtension(); |
| } |
| |
| bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (command_id == ID_QUIT) { |
| delegate_->OnExit(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ViewsWindow::OnKeyEvent(CefRefPtr<CefWindow> window, |
| const CefKeyEvent& event) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (!window_) |
| return false; |
| |
| if (delegate_->WithExtension() && event.type == KEYEVENT_RAWKEYDOWN && |
| event.windows_key_code == VK_ESCAPE) { |
| // Close the extension window on escape. |
| Close(false); |
| return true; |
| } |
| |
| if (!with_controls_) |
| return false; |
| |
| if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) { |
| // ALT key is pressed. |
| int last_focused_view = last_focused_view_; |
| bool menu_had_focus = menu_has_focus_; |
| |
| // Toggle menu button focusable. |
| SetMenuFocusable(!menu_has_focus_); |
| |
| if (menu_had_focus && last_focused_view != 0) { |
| // Restore focus to the view that was previously focused. |
| window_->GetViewForID(last_focused_view)->RequestFocus(); |
| } |
| |
| return true; |
| } |
| |
| if (menu_has_focus_ && top_menu_bar_) |
| return top_menu_bar_->OnKeyEvent(event); |
| |
| return false; |
| } |
| |
| CefSize ViewsWindow::GetMinimumSize(CefRefPtr<CefView> view) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (view->GetID() == ID_WINDOW) |
| return minimum_window_size_; |
| |
| return CefSize(); |
| } |
| |
| void ViewsWindow::OnFocus(CefRefPtr<CefView> view) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| const int view_id = view->GetID(); |
| |
| // Keep track of the non-menu view that was last focused. |
| if (last_focused_view_ != view_id && |
| (!top_menu_bar_ || !top_menu_bar_->HasMenuId(view_id))) { |
| last_focused_view_ = view_id; |
| } |
| |
| // When focus leaves the menu buttons make them unfocusable. |
| if (menu_has_focus_) { |
| if (top_menu_bar_) { |
| if (!top_menu_bar_->HasMenuId(view_id)) |
| SetMenuFocusable(false); |
| } else if (view_id != ID_MENU_BUTTON) { |
| SetMenuFocusable(false); |
| } |
| } |
| |
| if (view_id == ID_BROWSER_VIEW) |
| delegate_->OnViewsWindowActivated(this); |
| } |
| |
| void ViewsWindow::OnBlur(CefRefPtr<CefView> view) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| const int view_id = view->GetID(); |
| if (view_id == ID_BROWSER_VIEW && delegate_->WithExtension()) { |
| // Close windows hosting extensions when the browser loses focus. |
| Close(false); |
| } |
| } |
| |
| void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model, |
| int command_id, |
| cef_event_flags_t event_flags) { |
| ExecuteCommand(menu_model, command_id, event_flags); |
| } |
| |
| ViewsWindow::ViewsWindow(Delegate* delegate, |
| CefRefPtr<CefBrowserView> browser_view) |
| : delegate_(delegate), |
| with_controls_(false), |
| menu_has_focus_(false), |
| last_focused_view_(false) { |
| DCHECK(delegate_); |
| if (browser_view) |
| SetBrowserView(browser_view); |
| |
| CefRefPtr<CefCommandLine> command_line = |
| CefCommandLine::GetGlobalCommandLine(); |
| frameless_ = command_line->HasSwitch(switches::kHideFrame) || |
| delegate_->WithExtension(); |
| |
| if (!command_line->HasSwitch(switches::kHideTopMenu)) { |
| top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST); |
| } |
| } |
| |
| void ViewsWindow::SetBrowserView(CefRefPtr<CefBrowserView> browser_view) { |
| DCHECK(!browser_view_); |
| DCHECK(browser_view); |
| DCHECK(browser_view->IsValid()); |
| DCHECK(!browser_view->IsAttached()); |
| browser_view_ = browser_view; |
| browser_view_->SetID(ID_BROWSER_VIEW); |
| } |
| |
| void ViewsWindow::CreateMenuModel() { |
| // Create the menu button model. |
| button_menu_model_ = CefMenuModel::CreateMenuModel(this); |
| CefRefPtr<CefMenuModel> test_menu = |
| button_menu_model_->AddSubMenu(0, "&Tests"); |
| views_style::ApplyTo(button_menu_model_); |
| AddTestMenuItems(test_menu); |
| AddFileMenuItems(button_menu_model_); |
| |
| if (top_menu_bar_) { |
| // Add the menus to the top menu bar. |
| AddFileMenuItems(top_menu_bar_->CreateMenuModel("&File", nullptr)); |
| AddTestMenuItems(top_menu_bar_->CreateMenuModel("&Tests", nullptr)); |
| } |
| } |
| |
| CefRefPtr<CefLabelButton> ViewsWindow::CreateBrowseButton( |
| const std::string& label, |
| int id) { |
| CefRefPtr<CefLabelButton> button = |
| CefLabelButton::CreateLabelButton(this, label); |
| button->SetID(id); |
| button->SetEnabled(false); // Disabled by default. |
| button->SetFocusable(false); // Don't give focus to the button. |
| |
| return button; |
| } |
| |
| void ViewsWindow::AddControls() { |
| // Create the MenuModel that will be displayed via the menu button. |
| CreateMenuModel(); |
| |
| CefRefPtr<CefPanel> top_menu_panel; |
| if (top_menu_bar_) |
| top_menu_panel = top_menu_bar_->GetMenuPanel(); |
| |
| // Create the browse buttons. |
| LabelButtons browse_buttons; |
| browse_buttons.push_back(CreateBrowseButton("Back", ID_BACK_BUTTON)); |
| browse_buttons.push_back(CreateBrowseButton("Forward", ID_FORWARD_BUTTON)); |
| browse_buttons.push_back(CreateBrowseButton("Reload", ID_RELOAD_BUTTON)); |
| browse_buttons.push_back(CreateBrowseButton("Stop", ID_STOP_BUTTON)); |
| |
| // Create the URL textfield. |
| CefRefPtr<CefTextfield> url_textfield = CefTextfield::CreateTextfield(this); |
| url_textfield->SetID(ID_URL_TEXTFIELD); |
| url_textfield->SetEnabled(false); // Disabled by default. |
| views_style::ApplyTo(url_textfield); |
| |
| // Create the menu button. |
| CefRefPtr<CefMenuButton> menu_button = |
| CefMenuButton::CreateMenuButton(this, CefString()); |
| menu_button->SetID(ID_MENU_BUTTON); |
| menu_button->SetImage( |
| CEF_BUTTON_STATE_NORMAL, |
| delegate_->GetImageCache()->GetCachedImage("menu_icon")); |
| views_style::ApplyTo(menu_button.get()); |
| menu_button->SetInkDropEnabled(true); |
| // Override the default minimum size. |
| menu_button->SetMinimumSize(CefSize(0, 0)); |
| |
| // Create the top panel. |
| CefRefPtr<CefPanel> top_panel = CefPanel::CreatePanel(nullptr); |
| |
| // Use a horizontal box layout for |top_panel|. |
| CefBoxLayoutSettings top_panel_layout_settings; |
| top_panel_layout_settings.horizontal = true; |
| CefRefPtr<CefBoxLayout> top_panel_layout = |
| top_panel->SetToBoxLayout(top_panel_layout_settings); |
| |
| // Add the buttons and URL textfield to |top_panel|. |
| for (size_t i = 0U; i < browse_buttons.size(); ++i) |
| top_panel->AddChildView(browse_buttons[i]); |
| top_panel->AddChildView(url_textfield); |
| |
| UpdateExtensionControls(); |
| DCHECK(extensions_panel_); |
| top_panel->AddChildView(extensions_panel_); |
| |
| top_panel->AddChildView(menu_button); |
| views_style::ApplyTo(top_panel); |
| |
| // Allow |url_textfield| to grow and fill any remaining space. |
| top_panel_layout->SetFlexForView(url_textfield, 1); |
| |
| // Use a vertical box layout for |window|. |
| CefBoxLayoutSettings window_layout_settings; |
| window_layout_settings.horizontal = false; |
| window_layout_settings.between_child_spacing = 2; |
| CefRefPtr<CefBoxLayout> window_layout = |
| window_->SetToBoxLayout(window_layout_settings); |
| |
| // Add the top panel and browser view to |window|. |
| if (top_menu_panel) |
| window_->AddChildView(top_menu_panel); |
| window_->AddChildView(top_panel); |
| window_->AddChildView(browser_view_); |
| |
| // Allow |browser_view_| to grow and fill any remaining space. |
| window_layout->SetFlexForView(browser_view_, 1); |
| |
| // Lay out |window| so we can get the default button sizes. |
| window_->Layout(); |
| |
| // Make all browse buttons the same size. |
| MakeButtonsSameSize(browse_buttons); |
| |
| // Lay out |window| again with the new button sizes. |
| window_->Layout(); |
| |
| // Minimum window width is the size of all buttons plus some extra. |
| const int min_width = browse_buttons[0]->GetBounds().width * 4 + |
| menu_button->GetBounds().width + 100; |
| // Minimum window height is the hight of the top toolbar plus some extra. |
| int min_height = top_panel->GetBounds().height + 100; |
| if (top_menu_panel) |
| min_height += top_menu_panel->GetBounds().height; |
| |
| minimum_window_size_ = CefSize(min_width, min_height); |
| } |
| |
| void ViewsWindow::AddAccelerators() { |
| // Trigger accelerators without first forwarding to web content. |
| browser_view_->SetPreferAccelerators(true); |
| |
| // Specify the accelerators to handle. OnAccelerator will be called when the |
| // accelerator is triggered. |
| window_->SetAccelerator(ID_QUIT, 'X', false, false, true); |
| } |
| |
| void ViewsWindow::SetMenuFocusable(bool focusable) { |
| if (!window_ || !with_controls_) |
| return; |
| |
| if (top_menu_bar_) { |
| top_menu_bar_->SetMenuFocusable(focusable); |
| } else { |
| window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable); |
| |
| if (focusable) { |
| // Give focus to menu button. |
| window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus(); |
| } |
| } |
| |
| menu_has_focus_ = focusable; |
| } |
| |
| void ViewsWindow::EnableView(int id, bool enable) { |
| if (!window_) |
| return; |
| CefRefPtr<CefView> view = window_->GetViewForID(id); |
| if (view) |
| view->SetEnabled(enable); |
| } |
| |
| void ViewsWindow::ShowTopControls(bool show) { |
| if (!window_ || !with_controls_) |
| return; |
| |
| // Change the visibility of the panel that contains the buttons. |
| CefRefPtr<CefView> parent_view = |
| window_->GetViewForID(ID_BACK_BUTTON)->GetParentView(); |
| if (parent_view->IsVisible() != show) { |
| parent_view->SetVisible(show); |
| parent_view->InvalidateLayout(); |
| } |
| } |
| |
| void ViewsWindow::UpdateExtensionControls() { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (!window_ || !with_controls_) |
| return; |
| |
| if (!extensions_panel_) { |
| extensions_panel_ = CefPanel::CreatePanel(nullptr); |
| |
| // Use a horizontal box layout for |top_panel|. |
| CefBoxLayoutSettings top_panel_layout_settings; |
| top_panel_layout_settings.horizontal = true; |
| CefRefPtr<CefBoxLayout> top_panel_layout = |
| extensions_panel_->SetToBoxLayout(top_panel_layout_settings); |
| } else { |
| extensions_panel_->RemoveAllChildViews(); |
| } |
| |
| if (extensions_.size() > |
| ID_EXTENSION_BUTTON_LAST - ID_EXTENSION_BUTTON_FIRST) { |
| LOG(WARNING) << "Too many extensions loaded. Some will be ignored."; |
| } |
| |
| ExtensionInfoSet::const_iterator it = extensions_.begin(); |
| for (int id = ID_EXTENSION_BUTTON_FIRST; |
| it != extensions_.end() && id <= ID_EXTENSION_BUTTON_LAST; ++id, ++it) { |
| CefRefPtr<CefMenuButton> button = |
| CefMenuButton::CreateMenuButton(this, CefString()); |
| button->SetID(id); |
| button->SetImage(CEF_BUTTON_STATE_NORMAL, (*it).image_); |
| views_style::ApplyTo(button.get()); |
| button->SetInkDropEnabled(true); |
| // Override the default minimum size. |
| button->SetMinimumSize(CefSize(0, 0)); |
| |
| extensions_panel_->AddChildView(button); |
| } |
| |
| CefRefPtr<CefView> parent_view = extensions_panel_->GetParentView(); |
| if (parent_view) |
| parent_view->InvalidateLayout(); |
| } |
| |
| void ViewsWindow::OnExtensionIconsLoaded(const ExtensionSet& extensions, |
| const ImageCache::ImageSet& images) { |
| if (!CefCurrentlyOn(TID_UI)) { |
| // Execute this method on the UI thread. |
| CefPostTask(TID_UI, base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this, |
| extensions, images)); |
| return; |
| } |
| |
| DCHECK_EQ(extensions.size(), images.size()); |
| |
| extensions_.clear(); |
| |
| ExtensionSet::const_iterator it1 = extensions.begin(); |
| ImageCache::ImageSet::const_iterator it2 = images.begin(); |
| for (; it1 != extensions.end() && it2 != images.end(); ++it1, ++it2) { |
| CefRefPtr<CefImage> icon = *it2; |
| if (!icon) |
| icon = delegate_->GetImageCache()->GetCachedImage(kDefaultExtensionIcon); |
| extensions_.push_back(ExtensionInfo(*it1, icon)); |
| } |
| |
| UpdateExtensionControls(); |
| } |
| |
| void ViewsWindow::OnExtensionWindowClosed() { |
| if (!CefCurrentlyOn(TID_UI)) { |
| // Execute this method on the UI thread. |
| CefPostTask(TID_UI, |
| base::Bind(&ViewsWindow::OnExtensionWindowClosed, this)); |
| return; |
| } |
| |
| // Restore the button state. |
| extension_button_pressed_lock_ = nullptr; |
| } |
| |
| } // namespace client |