1471 lines
42 KiB
C++
1471 lines
42 KiB
C++
// 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_build.h"
|
|
#include "include/base/cef_callback.h"
|
|
#include "include/cef_app.h"
|
|
#include "include/cef_i18n_util.h"
|
|
#include "include/views/cef_box_layout.h"
|
|
#include "include/wrapper/cef_helpers.h"
|
|
#include "tests/cefclient/browser/main_context.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";
|
|
|
|
// Default window size.
|
|
constexpr int kDefaultWidth = 800;
|
|
constexpr int kDefaultHeight = 600;
|
|
|
|
#if defined(OS_MAC)
|
|
constexpr int kTitleBarHeight = 35;
|
|
constexpr int kWindowButtonsWidth = 80;
|
|
#endif
|
|
|
|
// 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 (const auto& button : buttons) {
|
|
const CefSize& button_size = button->GetPreferredSize();
|
|
if (size.width < button_size.width) {
|
|
size.width = button_size.width;
|
|
}
|
|
if (size.height < button_size.height) {
|
|
size.height = button_size.height;
|
|
}
|
|
}
|
|
|
|
for (const auto& button : buttons) {
|
|
// Set the button's minimum size.
|
|
button->SetMinimumSize(size);
|
|
|
|
// Re-layout the button and all parent Views.
|
|
button->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_WINDOW_DIALOG, "Dialog Window");
|
|
test_menu->AddItem(ID_TESTS_REQUEST, "Request");
|
|
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);
|
|
}
|
|
|
|
CefBrowserViewDelegate::ChromeToolbarType CalculateChromeToolbarType(
|
|
const std::string& toolbar_type,
|
|
bool hide_toolbar,
|
|
bool with_overlay_controls) {
|
|
if (!MainContext::Get()->UseChromeRuntime() || toolbar_type == "none" ||
|
|
hide_toolbar) {
|
|
return CEF_CTT_NONE;
|
|
}
|
|
|
|
if (toolbar_type == "location") {
|
|
return CEF_CTT_LOCATION;
|
|
}
|
|
|
|
return with_overlay_controls ? CEF_CTT_LOCATION : CEF_CTT_NORMAL;
|
|
}
|
|
|
|
void SetViewEnabled(CefRefPtr<CefWindow> window, int id, bool enable) {
|
|
if (auto view = window->GetViewForID(id)) {
|
|
view->SetEnabled(enable);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
CefRefPtr<ViewsWindow> ViewsWindow::Create(
|
|
WindowType type,
|
|
Delegate* delegate,
|
|
CefRefPtr<CefClient> client,
|
|
const CefString& url,
|
|
const CefBrowserSettings& settings,
|
|
CefRefPtr<CefRequestContext> request_context,
|
|
CefRefPtr<CefCommandLine> command_line) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(delegate);
|
|
|
|
// Create a new ViewsWindow.
|
|
CefRefPtr<ViewsWindow> views_window =
|
|
new ViewsWindow(type, delegate, nullptr, command_line);
|
|
|
|
// 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_) {
|
|
if (type_ == WindowType::DIALOG) {
|
|
if (use_window_modal_dialog_) {
|
|
// Show as a window modal dialog (IsWindowModalDialog() will return
|
|
// true).
|
|
window_->Show();
|
|
} else {
|
|
CefRefPtr<CefBrowserView> browser_view;
|
|
if (auto parent_window = delegate_->GetParentWindow()) {
|
|
if (auto view = parent_window->GetViewForID(ID_BROWSER_VIEW)) {
|
|
browser_view = view->AsBrowserView();
|
|
}
|
|
}
|
|
CHECK(browser_view);
|
|
|
|
// Show as a browser modal dialog (relative to |browser_view|).
|
|
window_->ShowAsBrowserModalDialog(browser_view);
|
|
}
|
|
} else {
|
|
window_->Show();
|
|
}
|
|
}
|
|
if (browser_view_ && !window_->IsMinimized()) {
|
|
// 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;
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
if (hide_on_close_) {
|
|
// Don't hide on close if we actually want to close.
|
|
hide_on_close_ = false;
|
|
}
|
|
#endif
|
|
|
|
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_) {
|
|
return;
|
|
}
|
|
|
|
// |location_bar_| may instead be a Chrome toolbar.
|
|
if (location_bar_ && location_bar_->AsTextfield()) {
|
|
location_bar_->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();
|
|
|
|
// For Chrome runtime we ignore this notification from
|
|
// ClientHandler::OnFullscreenModeChange(). Chrome runtime will trigger
|
|
// the fullscreen change internally and then call
|
|
// OnWindowFullscreenTransition().
|
|
if (MainContext::Get()->UseChromeRuntime()) {
|
|
return;
|
|
}
|
|
|
|
// For Alloy runtime we need to explicitly trigger the fullscreen change.
|
|
if (window_) {
|
|
// Results in a call to OnWindowFullscreenTransition().
|
|
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();
|
|
|
|
is_loading_ = isLoading;
|
|
can_go_back_ = canGoBack;
|
|
can_go_forward_ = canGoForward;
|
|
|
|
if (!window_ || chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
return;
|
|
}
|
|
|
|
// |toolbar_| may be nullptr for the initial notification after CefBrowser
|
|
// creation, in which case the initial state will be appled in AddControls.
|
|
if (with_controls_ && toolbar_) {
|
|
UpdateToolbarButtonState();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetDraggableRegions(
|
|
const std::vector<CefDraggableRegion>& regions) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_ || !browser_view_) {
|
|
return;
|
|
}
|
|
|
|
// Convert the regions from BrowserView to Window coordinates.
|
|
std::vector<CefDraggableRegion> window_regions = regions;
|
|
for (auto& region : window_regions) {
|
|
CefPoint origin = CefPoint(region.bounds.x, region.bounds.y);
|
|
browser_view_->ConvertPointToWindow(origin);
|
|
region.bounds.x = origin.x;
|
|
region.bounds.y = origin.y;
|
|
}
|
|
|
|
if (overlay_controls_) {
|
|
// Exclude all regions obscured by overlays.
|
|
overlay_controls_->UpdateDraggableRegions(window_regions);
|
|
}
|
|
|
|
window_->SetDraggableRegions(window_regions);
|
|
}
|
|
|
|
void ViewsWindow::TakeFocus(bool next) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_) {
|
|
return;
|
|
}
|
|
|
|
if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
toolbar_->RequestFocus();
|
|
} else if (location_bar_) {
|
|
// Give focus to the location bar.
|
|
location_bar_->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 nullptr image and use the default icon.
|
|
image_set.push_back(ImageCache::ImageInfo::Empty());
|
|
}
|
|
}
|
|
|
|
delegate_->GetImageCache()->LoadImages(
|
|
image_set,
|
|
base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
|
|
}
|
|
|
|
// static
|
|
bool ViewsWindow::SupportsWindowRestore(WindowType type) {
|
|
// Only support window restore with normal windows.
|
|
return type == WindowType::NORMAL;
|
|
}
|
|
|
|
bool ViewsWindow::SupportsWindowRestore() const {
|
|
return SupportsWindowRestore(type_);
|
|
}
|
|
|
|
bool ViewsWindow::GetWindowRestorePreferences(
|
|
cef_show_state_t& show_state,
|
|
std::optional<CefRect>& dip_bounds) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(SupportsWindowRestore());
|
|
if (!window_) {
|
|
return false;
|
|
}
|
|
|
|
show_state = CEF_SHOW_STATE_NORMAL;
|
|
if (window_->IsMinimized()) {
|
|
show_state = CEF_SHOW_STATE_MINIMIZED;
|
|
} else if (window_->IsFullscreen()) {
|
|
// On MacOS, IsMaximized() will also return true for fullscreen, so check
|
|
// IsFullscreen() first.
|
|
show_state = CEF_SHOW_STATE_FULLSCREEN;
|
|
} else if (window_->IsMaximized()) {
|
|
show_state = CEF_SHOW_STATE_MAXIMIZED;
|
|
}
|
|
|
|
if (show_state == CEF_SHOW_STATE_NORMAL) {
|
|
// Use the current visible bounds.
|
|
dip_bounds = window_->GetBoundsInScreen();
|
|
} else {
|
|
// Use the last known visible bounds.
|
|
dip_bounds = last_visible_bounds_;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ViewsWindow::SetTitlebarHeight(const std::optional<float>& height) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (height.has_value()) {
|
|
override_titlebar_height_ = height;
|
|
} else {
|
|
override_titlebar_height_ = default_titlebar_height_;
|
|
}
|
|
NudgeWindow();
|
|
}
|
|
|
|
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);
|
|
|
|
// May be nullptr when using the default popup behavior.
|
|
if (!popup_delegate) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Should not be the same RootWindowViews that owns |this|.
|
|
DCHECK(popup_delegate != delegate_);
|
|
|
|
// Create a new ViewsWindow for the popup BrowserView.
|
|
return new ViewsWindow(
|
|
is_devtools ? WindowType::DEVTOOLS : WindowType::NORMAL, popup_delegate,
|
|
nullptr, command_line_);
|
|
}
|
|
|
|
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()));
|
|
|
|
// May be nullptr when using the default popup behavior.
|
|
if (!popup_window) {
|
|
return false;
|
|
}
|
|
|
|
// Should not be the same ViewsWindow as |this|.
|
|
DCHECK(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;
|
|
}
|
|
|
|
CefBrowserViewDelegate::ChromeToolbarType ViewsWindow::GetChromeToolbarType(
|
|
CefRefPtr<CefBrowserView> browser_view) {
|
|
return chrome_toolbar_type_;
|
|
}
|
|
|
|
bool ViewsWindow::UseFramelessWindowForPictureInPicture(
|
|
CefRefPtr<CefBrowserView> browser_view) {
|
|
return hide_pip_frame_;
|
|
}
|
|
|
|
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::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
|
|
return;
|
|
}
|
|
|
|
DCHECK(with_controls_ || with_overlay_controls_);
|
|
DCHECK_EQ(ID_MENU_BUTTON, id);
|
|
|
|
const auto button_bounds = menu_button->GetBoundsInScreen();
|
|
|
|
auto point = screen_point;
|
|
if (with_overlay_controls_) {
|
|
// Align the menu correctly under the button.
|
|
if (CefIsRTL()) {
|
|
point.x += button_bounds.width - 4;
|
|
} else {
|
|
point.x -= button_bounds.width - 4;
|
|
}
|
|
}
|
|
|
|
if (use_bottom_controls_) {
|
|
const auto display_bounds =
|
|
menu_button->GetWindow()->GetDisplay()->GetWorkArea();
|
|
const int available_height = display_bounds.y + display_bounds.height -
|
|
button_bounds.y - button_bounds.height;
|
|
|
|
// Approximation of the menu height.
|
|
const int menu_height =
|
|
static_cast<int>(button_menu_model_->GetCount()) * button_bounds.height;
|
|
if (menu_height > available_height) {
|
|
// The menu will go upwards, so place it above the button.
|
|
point.y -= button_bounds.height - 8;
|
|
}
|
|
}
|
|
|
|
menu_button->ShowMenu(button_menu_model_, point,
|
|
with_overlay_controls_ ? CEF_MENU_ANCHOR_TOPLEFT
|
|
: 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_ || with_overlay_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_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) {
|
|
const CefString& url = textfield->GetText();
|
|
if (!url.empty()) {
|
|
browser->GetMainFrame()->LoadURL(url);
|
|
}
|
|
}
|
|
|
|
// We handled the event.
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ViewsWindow::OnWindowFullscreenTransition(CefRefPtr<CefWindow> window,
|
|
bool is_completed) {
|
|
#if defined(OS_MAC)
|
|
// On MacOS we get two asynchronous callbacks, and we want to change the UI on
|
|
// |is_completed=false| (e.g. when the fullscreen transition begins).
|
|
const bool should_change = !is_completed;
|
|
#else
|
|
// On other platforms we only get a single synchronous callback with
|
|
// |is_completed=true|.
|
|
DCHECK(is_completed);
|
|
const bool should_change = true;
|
|
#endif
|
|
|
|
// Hide the top controls while in fullscreen mode.
|
|
if (should_change && with_controls_) {
|
|
ShowTopControls(!window->IsFullscreen());
|
|
}
|
|
|
|
// With Alloy runtime we need to explicitly exit browser fullscreen when
|
|
// exiting window fullscreen. Chrome runtime handles this internally.
|
|
if (!MainContext::Get()->UseChromeRuntime() && should_change &&
|
|
!window->IsFullscreen()) {
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (browser && browser->GetHost()->IsFullscreen()) {
|
|
// Will not cause a resize because the fullscreen transition has already
|
|
// begun.
|
|
browser->GetHost()->ExitFullscreen(/*will_cause_resize=*/false);
|
|
}
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
// Continue hide logic from CanClose.
|
|
if (is_completed && hide_after_fullscreen_exit_) {
|
|
hide_after_fullscreen_exit_ = false;
|
|
window->Hide();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(browser_view_);
|
|
DCHECK(!window_);
|
|
DCHECK(window);
|
|
|
|
window_ = window;
|
|
window_->SetID(ID_WINDOW);
|
|
|
|
delegate_->OnViewsWindowCreated(this);
|
|
|
|
if (type_ == WindowType::NORMAL || type_ == WindowType::DEVTOOLS) {
|
|
const CefRect bounds = delegate_->GetInitialBounds();
|
|
if (bounds.IsEmpty()) {
|
|
// Size the Window and center it at the default size.
|
|
window_->CenterWindow(CefSize(kDefaultWidth, kDefaultHeight));
|
|
} else if (SupportsWindowRestore()) {
|
|
// Remember the bounds from the previous application run in case the user
|
|
// does not move or resize the window during this application run.
|
|
last_visible_bounds_ = bounds;
|
|
}
|
|
}
|
|
|
|
// Set the background color for regions that are not obscured by other Views.
|
|
views_style::ApplyTo(window_.get());
|
|
|
|
if (with_controls_ || with_overlay_controls_) {
|
|
// Create the MenuModel that will be displayed via the menu button.
|
|
CreateMenuModel();
|
|
}
|
|
|
|
if (with_controls_) {
|
|
// Add the BrowserView to the Window. Other controls will be added after the
|
|
// BrowserView is added.
|
|
AddBrowserView();
|
|
|
|
// Add keyboard accelerators to the Window.
|
|
AddAccelerators();
|
|
|
|
// Hide the top controls while in full-screen mode.
|
|
if (delegate_->GetInitialShowState() == CEF_SHOW_STATE_FULLSCREEN) {
|
|
ShowTopControls(false);
|
|
}
|
|
} else {
|
|
// Add the BrowserView as the only child of the Window.
|
|
window_->AddChildView(browser_view_);
|
|
|
|
if (type_ != WindowType::EXTENSION) {
|
|
// Choose a reasonable minimum window size.
|
|
minimum_window_size_ = CefSize(100, 100);
|
|
}
|
|
}
|
|
|
|
if (!delegate_->InitiallyHidden()) {
|
|
// Show the Window.
|
|
Show();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnWindowClosing(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(window_);
|
|
|
|
delegate_->OnViewsWindowClosing(this);
|
|
}
|
|
|
|
void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(window_);
|
|
|
|
delegate_->OnViewsWindowDestroyed(this);
|
|
|
|
browser_view_ = nullptr;
|
|
button_menu_model_ = nullptr;
|
|
if (menu_bar_) {
|
|
menu_bar_->Reset();
|
|
menu_bar_ = nullptr;
|
|
}
|
|
extensions_panel_ = nullptr;
|
|
menu_button_ = nullptr;
|
|
window_ = nullptr;
|
|
}
|
|
|
|
void ViewsWindow::OnWindowActivationChanged(CefRefPtr<CefWindow> window,
|
|
bool active) {
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
delegate_->OnViewsWindowActivated(this);
|
|
}
|
|
|
|
void ViewsWindow::OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
|
|
const CefRect& new_bounds) {
|
|
if (SupportsWindowRestore() && !window->IsMinimized() &&
|
|
!window->IsMaximized() && !window->IsFullscreen()) {
|
|
// Track the last visible bounds for window restore purposes.
|
|
last_visible_bounds_ = new_bounds;
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_ && toolbar_) {
|
|
auto insets = toolbar_->GetInsets();
|
|
insets.left = window->IsFullscreen() ? 0 : kWindowButtonsWidth;
|
|
toolbar_->SetInsets(insets);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
#if defined(OS_MAC)
|
|
// On MacOS we might hide the window instead of closing it.
|
|
if (hide_on_close_) {
|
|
if (window->IsFullscreen()) {
|
|
// Need to exit fullscreen mode before hiding the window.
|
|
// Execution continues in OnWindowFullscreenTransition.
|
|
hide_after_fullscreen_exit_ = true;
|
|
window->SetFullscreen(false);
|
|
} else {
|
|
window->Hide();
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// 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) {
|
|
// Extension windows behave as a menu and allow activation.
|
|
if (type_ == WindowType::EXTENSION) {
|
|
*is_menu = true;
|
|
*can_activate_menu = true;
|
|
}
|
|
}
|
|
return parent_window;
|
|
}
|
|
|
|
bool ViewsWindow::IsWindowModalDialog(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(delegate_->GetParentWindow());
|
|
return use_window_modal_dialog_;
|
|
}
|
|
|
|
CefRect ViewsWindow::GetInitialBounds(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
const CefRect bounds = delegate_->GetInitialBounds();
|
|
if (frameless_ && bounds.IsEmpty()) {
|
|
// Need to provide a size for frameless windows that will be centered.
|
|
return CefRect(0, 0, kDefaultWidth, kDefaultHeight);
|
|
}
|
|
return bounds;
|
|
}
|
|
|
|
cef_show_state_t ViewsWindow::GetInitialShowState(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return delegate_->GetInitialShowState();
|
|
}
|
|
|
|
bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return frameless_;
|
|
}
|
|
|
|
bool ViewsWindow::WithStandardWindowButtons(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return with_standard_buttons_;
|
|
}
|
|
|
|
bool ViewsWindow::GetTitlebarHeight(CefRefPtr<CefWindow> window,
|
|
float* titlebar_height) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
#if defined(OS_MAC)
|
|
if (override_titlebar_height_.has_value()) {
|
|
*titlebar_height = override_titlebar_height_.value();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// Only allow resize of normal and DevTools windows.
|
|
return type_ == WindowType::NORMAL || type_ == WindowType::DEVTOOLS;
|
|
}
|
|
|
|
bool ViewsWindow::CanMaximize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return CanResize(window);
|
|
}
|
|
|
|
bool ViewsWindow::CanMinimize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return CanResize(window);
|
|
}
|
|
|
|
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 (type_ == WindowType::EXTENSION && 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_ && menu_bar_) {
|
|
return menu_bar_->OnKeyEvent(event);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CefSize ViewsWindow::GetPreferredSize(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (view->GetID() == ID_WINDOW && type_ == WindowType::DIALOG) {
|
|
// Preferred size for a browser modal dialog. The dialog will be shrunk to
|
|
// fit inside the parent browser view if necessary.
|
|
return CefSize(kDefaultWidth, kDefaultHeight);
|
|
}
|
|
|
|
return CefSize();
|
|
}
|
|
|
|
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 &&
|
|
(!menu_bar_ || !menu_bar_->HasMenuId(view_id))) {
|
|
last_focused_view_ = view_id;
|
|
}
|
|
|
|
// When focus leaves the menu buttons make them unfocusable.
|
|
if (menu_has_focus_) {
|
|
if (menu_bar_) {
|
|
if (!menu_bar_->HasMenuId(view_id)) {
|
|
SetMenuFocusable(false);
|
|
}
|
|
} else if (view_id != ID_MENU_BUTTON) {
|
|
SetMenuFocusable(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnBlur(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
const int view_id = view->GetID();
|
|
if (view_id == ID_BROWSER_VIEW && type_ == WindowType::EXTENSION) {
|
|
// Close windows hosting extensions when the browser loses focus.
|
|
Close(false);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
|
|
const int view_id = view->GetID();
|
|
if (view_id != ID_BROWSER_VIEW) {
|
|
return;
|
|
}
|
|
|
|
if (added) {
|
|
if (with_controls_) {
|
|
AddControls();
|
|
}
|
|
|
|
if (with_overlay_controls_) {
|
|
// Add window buttons if we don't have standard ones
|
|
const bool with_window_buttons = !with_standard_buttons_;
|
|
overlay_controls_ =
|
|
new ViewsOverlayControls(with_window_buttons, use_bottom_controls_);
|
|
overlay_controls_->Initialize(window_, CreateMenuButton(),
|
|
CreateLocationBar(),
|
|
chrome_toolbar_type_ != CEF_CTT_NONE);
|
|
}
|
|
} else {
|
|
// Remove any controls that may include the Chrome toolbar before removing
|
|
// the BrowserView.
|
|
if (overlay_controls_) {
|
|
overlay_controls_->Destroy();
|
|
overlay_controls_ = nullptr;
|
|
location_bar_ = nullptr;
|
|
} else if (use_bottom_controls_) {
|
|
if (toolbar_) {
|
|
window_->RemoveChildView(toolbar_);
|
|
toolbar_ = nullptr;
|
|
location_bar_ = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnLayoutChanged(CefRefPtr<CefView> view,
|
|
const CefRect& new_bounds) {
|
|
const int view_id = view->GetID();
|
|
if (view_id != ID_BROWSER_VIEW) {
|
|
return;
|
|
}
|
|
|
|
if (overlay_controls_) {
|
|
overlay_controls_->UpdateControls();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnThemeChanged(CefRefPtr<CefView> view) {
|
|
// Apply colors when the theme changes.
|
|
views_style::ApplyTo(view);
|
|
}
|
|
|
|
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(WindowType type,
|
|
Delegate* delegate,
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
CefRefPtr<CefCommandLine> command_line)
|
|
: type_(type), delegate_(delegate), command_line_(command_line) {
|
|
DCHECK(delegate_);
|
|
|
|
if (browser_view) {
|
|
SetBrowserView(browser_view);
|
|
}
|
|
|
|
const bool is_normal_type = type_ == WindowType::NORMAL;
|
|
|
|
with_controls_ = is_normal_type && delegate_->WithControls();
|
|
|
|
const bool hide_frame = command_line->HasSwitch(switches::kHideFrame);
|
|
const bool hide_overlays =
|
|
!is_normal_type || command_line->HasSwitch(switches::kHideOverlays);
|
|
const bool hide_toolbar = hide_overlays && !with_controls_;
|
|
const bool show_window_buttons =
|
|
command_line->HasSwitch(switches::kShowWindowButtons);
|
|
|
|
// Without a window frame.
|
|
frameless_ = hide_frame || type_ == WindowType::EXTENSION;
|
|
|
|
// With an overlay that mimics window controls.
|
|
with_overlay_controls_ = hide_frame && !hide_overlays && !with_controls_;
|
|
|
|
// If window has frame or flag passed explicitly
|
|
with_standard_buttons_ = !frameless_ || show_window_buttons;
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_) {
|
|
default_titlebar_height_ = kTitleBarHeight;
|
|
override_titlebar_height_ = kTitleBarHeight;
|
|
}
|
|
|
|
hide_on_close_ = command_line->HasSwitch(switches::kHideWindowOnClose);
|
|
#endif
|
|
|
|
const std::string& toolbar_type =
|
|
command_line->GetSwitchValue(switches::kShowChromeToolbar);
|
|
chrome_toolbar_type_ = CalculateChromeToolbarType(toolbar_type, hide_toolbar,
|
|
with_overlay_controls_);
|
|
|
|
use_bottom_controls_ = command_line->HasSwitch(switches::kUseBottomControls);
|
|
|
|
#if !defined(OS_MAC)
|
|
// On Mac we don't show a top menu on the window. The options are available in
|
|
// the app menu instead.
|
|
if (!command_line->HasSwitch(switches::kHideTopMenu)) {
|
|
menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST, use_bottom_controls_);
|
|
}
|
|
#endif
|
|
|
|
use_window_modal_dialog_ =
|
|
command_line->HasSwitch(switches::kUseWindowModalDialog);
|
|
hide_pip_frame_ = command_line->HasSwitch(switches::kHidePipFrame);
|
|
}
|
|
|
|
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 (menu_bar_) {
|
|
// Add the menus to the top menu bar.
|
|
AddFileMenuItems(menu_bar_->CreateMenuModel("&File", nullptr));
|
|
AddTestMenuItems(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->SetInkDropEnabled(true);
|
|
button->SetEnabled(false); // Disabled by default.
|
|
button->SetFocusable(false); // Don't give focus to the button.
|
|
|
|
return button;
|
|
}
|
|
|
|
CefRefPtr<CefMenuButton> ViewsWindow::CreateMenuButton() {
|
|
// Create the menu button.
|
|
DCHECK(!menu_button_);
|
|
menu_button_ = CefMenuButton::CreateMenuButton(this, CefString());
|
|
menu_button_->SetID(ID_MENU_BUTTON);
|
|
menu_button_->SetImage(
|
|
CEF_BUTTON_STATE_NORMAL,
|
|
delegate_->GetImageCache()->GetCachedImage("menu_icon"));
|
|
menu_button_->SetInkDropEnabled(true);
|
|
// Override the default minimum size.
|
|
menu_button_->SetMinimumSize(CefSize(0, 0));
|
|
return menu_button_;
|
|
}
|
|
|
|
CefRefPtr<CefView> ViewsWindow::CreateLocationBar() {
|
|
DCHECK(!location_bar_);
|
|
if (chrome_toolbar_type_ == CEF_CTT_LOCATION) {
|
|
// Chrome will provide a minimal location bar.
|
|
location_bar_ = browser_view_->GetChromeToolbar();
|
|
DCHECK(location_bar_);
|
|
}
|
|
if (!location_bar_) {
|
|
// Create the URL textfield.
|
|
CefRefPtr<CefTextfield> url_textfield = CefTextfield::CreateTextfield(this);
|
|
url_textfield->SetID(ID_URL_TEXTFIELD);
|
|
location_bar_ = url_textfield;
|
|
}
|
|
return location_bar_;
|
|
}
|
|
|
|
void ViewsWindow::AddBrowserView() {
|
|
// 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);
|
|
|
|
window_->AddChildView(browser_view_);
|
|
|
|
// Allow |browser_view_| to grow and fill any remaining space.
|
|
window_layout->SetFlexForView(browser_view_, 1);
|
|
|
|
// Remaining setup will be performed in OnWindowChanged after the BrowserView
|
|
// is added to the CefWindow. This is necessary because Chrome toolbars are
|
|
// only available after the BrowserView is added.
|
|
}
|
|
|
|
void ViewsWindow::AddControls() {
|
|
// Build the remainder of the UI now that the BrowserView has been added to
|
|
// the CefWindow. This is a requirement to use Chrome toolbars.
|
|
|
|
CefRefPtr<CefPanel> menu_panel;
|
|
if (menu_bar_) {
|
|
menu_panel = menu_bar_->GetMenuPanel();
|
|
}
|
|
|
|
LabelButtons browse_buttons;
|
|
|
|
if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
// Chrome will provide a normal toolbar with location, menu, etc.
|
|
toolbar_ = browser_view_->GetChromeToolbar();
|
|
DCHECK(toolbar_);
|
|
}
|
|
|
|
if (!toolbar_) {
|
|
// Create the 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));
|
|
|
|
CreateLocationBar();
|
|
CreateMenuButton();
|
|
|
|
// Create the toolbar panel.
|
|
CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(nullptr);
|
|
|
|
// Use a horizontal box layout for |panel|.
|
|
CefBoxLayoutSettings panel_layout_settings;
|
|
panel_layout_settings.horizontal = true;
|
|
CefRefPtr<CefBoxLayout> panel_layout =
|
|
panel->SetToBoxLayout(panel_layout_settings);
|
|
|
|
// Add the buttons and URL textfield to |panel|.
|
|
for (auto& browse_button : browse_buttons) {
|
|
panel->AddChildView(browse_button);
|
|
}
|
|
panel->AddChildView(location_bar_);
|
|
|
|
UpdateExtensionControls();
|
|
DCHECK(extensions_panel_);
|
|
panel->AddChildView(extensions_panel_);
|
|
|
|
panel->AddChildView(menu_button_);
|
|
|
|
// Allow |location| to grow and fill any remaining space.
|
|
panel_layout->SetFlexForView(location_bar_, 1);
|
|
|
|
toolbar_ = panel;
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_) {
|
|
auto insets = toolbar_->GetInsets();
|
|
insets.left = kWindowButtonsWidth;
|
|
toolbar_->SetInsets(insets);
|
|
}
|
|
#endif
|
|
|
|
if (use_bottom_controls_) {
|
|
// Add the panel at the bottom of |window|.
|
|
window_->AddChildView(toolbar_);
|
|
if (menu_panel) {
|
|
window_->AddChildView(menu_panel);
|
|
}
|
|
} else {
|
|
// Add the panel at the top of |window|.
|
|
int index = 0;
|
|
if (menu_panel) {
|
|
window_->AddChildViewAt(menu_panel, index++);
|
|
}
|
|
window_->AddChildViewAt(toolbar_, index);
|
|
}
|
|
|
|
// Lay out |window| so we can get the default button sizes.
|
|
window_->Layout();
|
|
|
|
int min_width = 200;
|
|
if (!browse_buttons.empty()) {
|
|
// Make all browse buttons the same size.
|
|
MakeButtonsSameSize(browse_buttons);
|
|
|
|
// Lay out |window| again with the new button sizes.
|
|
window_->Layout();
|
|
|
|
const int buttons_number = static_cast<int>(browse_buttons.size());
|
|
|
|
// Minimum window width is the size of all buttons plus some extra.
|
|
min_width = browse_buttons[0]->GetBounds().width * buttons_number +
|
|
menu_button_->GetBounds().width + 100;
|
|
}
|
|
|
|
// Minimum window height is the hight of the toolbar plus some extra.
|
|
int min_height = toolbar_->GetBounds().height + 100;
|
|
if (menu_panel) {
|
|
min_height += menu_panel->GetBounds().height;
|
|
}
|
|
|
|
minimum_window_size_ = CefSize(min_width, min_height);
|
|
|
|
// Apply the state that we may have missed when SetLoadingState was called
|
|
// initially.
|
|
UpdateToolbarButtonState();
|
|
}
|
|
|
|
void ViewsWindow::AddAccelerators() {
|
|
// Specify the accelerators to handle. OnAccelerator will be called when the
|
|
// accelerator is triggered.
|
|
window_->SetAccelerator(ID_QUIT, 'X', /*shift_pressed=*/false,
|
|
/*ctrl_pressed=*/false, /*alt_pressed=*/true,
|
|
/*high_priority=*/true);
|
|
}
|
|
|
|
void ViewsWindow::SetMenuFocusable(bool focusable) {
|
|
if (!window_ || !with_controls_) {
|
|
return;
|
|
}
|
|
|
|
if (menu_bar_) {
|
|
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::UpdateToolbarButtonState() {
|
|
SetViewEnabled(window_, ID_BACK_BUTTON, can_go_back_);
|
|
SetViewEnabled(window_, ID_FORWARD_BUTTON, can_go_forward_);
|
|
SetViewEnabled(window_, ID_RELOAD_BUTTON, !is_loading_);
|
|
SetViewEnabled(window_, ID_STOP_BUTTON, is_loading_);
|
|
}
|
|
|
|
void ViewsWindow::ShowTopControls(bool show) {
|
|
if (!window_ || !with_controls_) {
|
|
return;
|
|
}
|
|
|
|
// Change the visibility of the toolbar.
|
|
if (toolbar_->IsVisible() != show) {
|
|
toolbar_->SetVisible(show);
|
|
toolbar_->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 |panel|.
|
|
CefBoxLayoutSettings panel_layout_settings;
|
|
panel_layout_settings.horizontal = true;
|
|
CefRefPtr<CefBoxLayout> panel_layout =
|
|
extensions_panel_->SetToBoxLayout(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_);
|
|
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::BindOnce(&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_.emplace_back(*it1, icon);
|
|
}
|
|
|
|
UpdateExtensionControls();
|
|
}
|
|
|
|
void ViewsWindow::OnExtensionWindowClosed() {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
// Execute this method on the UI thread.
|
|
CefPostTask(TID_UI,
|
|
base::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
|
|
return;
|
|
}
|
|
|
|
// Restore the button state.
|
|
extension_button_pressed_lock_ = nullptr;
|
|
}
|
|
|
|
#if !defined(OS_MAC)
|
|
void ViewsWindow::NudgeWindow() {
|
|
NOTIMPLEMENTED();
|
|
}
|
|
#endif
|
|
|
|
} // namespace client
|