cef/tests/cefclient/browser/views_window.cc
Marshall Greenblatt dca0435d2f chrome: Add support for Alloy style browsers and windows (see #3681)
Split the Alloy runtime into bootstrap and style components. Support
creation of Alloy style browsers and windows with the Chrome runtime.
Chrome runtime (`--enable-chrome-runtime`) + Alloy style
(`--use-alloy-style`) supports Views (`--use-views`), native parent
(`--use-native`) and windowless rendering
(`--off-screen-rendering-enabled`).

Print preview is supported in all cases except with windowless rendering
on all platforms and native parent on MacOS. It is disabled by default
with Alloy style for legacy compatibility. Where supported it can be
enabled or disabled globally using `--[enable|disable]-print-preview` or
configured on a per-RequestContext basis using the
`printing.print_preview_disabled` preference. It also behaves as
expected when triggered via the PDF viewer print button.

Chrome runtime + Alloy style behavior differs from Alloy runtime in the
following significant ways:

- Supports Chrome error pages by default.
- DevTools popups are Chrome style only (cannot be windowless).
- The Alloy extension API will not supported.

Chrome runtime + Alloy style passes all expected Alloy ceftests except
the following:

- `DisplayTest.AutoResize` (Alloy extension API not supported)
- `DownloadTest.*` (Download API not yet supported)
- `ExtensionTest.*` (Alloy extension API not supported)

This change also adds Chrome runtime support for
CefContextMenuHandler::RunContextMenu (see #3293).

This change also explicitly blocks (and doesn't retry) FrameAttached
requests from PDF viewer and print preview excluded frames (see #3664).

Known issues specific to Chrome runtime + Alloy style:
- DevTools popup with windowless rendering doesn't load successfully.
  Use windowed rendering or remote debugging as a workaround.
- Chrome style Window with Alloy style BrowserView (`--use-alloy-style
  --use-chrome-style-window`) does not show Chrome theme changes.

To test:
- Run `ceftests --enable-chrome-runtime --use-alloy-style
       [--use-chrome-style-window] [--use-views|--use-native]
       --gtest_filter=...`
- Run `cefclient --enable-chrome-runtime --use-alloy-style
       [--use-chrome-style-window]
       [--use-views|--use-native|--off-screen-rendering-enabled]`
- Run `cefsimple --enable-chrome-runtime --use-alloy-style [--use-views]`
2024-04-22 14:57:37 -04:00

1524 lines
44 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(
bool use_alloy_style,
const std::string& toolbar_type,
bool hide_toolbar,
bool with_overlay_controls) {
if (use_alloy_style || 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);
const auto expected_browser_runtime_style = views_window->use_alloy_style_
? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME;
const auto expected_window_runtime_style =
views_window->use_alloy_style_window_ ? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME;
// Create a new BrowserView.
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
client, url, settings, nullptr, request_context, views_window);
CHECK_EQ(expected_browser_runtime_style, browser_view->GetRuntimeStyle());
// Associate the BrowserView with the ViewsWindow.
views_window->SetBrowserView(browser_view);
// Create a new top-level Window. It will show itself after creation.
auto window = CefWindow::CreateTopLevelWindow(views_window);
CHECK_EQ(expected_window_runtime_style, window->GetRuntimeStyle());
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 style we ignore this notification from
// ClientHandler::OnFullscreenModeChange(). Chrome style will trigger
// the fullscreen change internally and then call
// OnWindowFullscreenTransition().
if (!use_alloy_style_) {
return;
}
// For Alloy style 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_;
}
cef_runtime_style_t ViewsWindow::GetBrowserRuntimeStyle() {
if (use_alloy_style_) {
return CEF_RUNTIME_STYLE_ALLOY;
}
return CEF_RUNTIME_STYLE_DEFAULT;
}
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 style we need to explicitly exit browser fullscreen when
// exiting window fullscreen. Chrome style handles this internally.
if (use_alloy_style_ && 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::OnThemeColorsChanged(CefRefPtr<CefWindow> window,
bool chrome_theme) {
// Apply color overrides to the current theme.
views_style::ApplyTo(window);
}
cef_runtime_style_t ViewsWindow::GetWindowRuntimeStyle() {
if (use_alloy_style_window_) {
return CEF_RUNTIME_STYLE_ALLOY;
}
return CEF_RUNTIME_STYLE_DEFAULT;
}
void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
DCHECK(browser_view_);
DCHECK(!window_);
DCHECK(window);
window_ = window;
window_->SetID(ID_WINDOW);
// Apply color overrides to the current native/OS theme. This is only
// necessary until the CefBrowserView is added to the CefWindow, at which time
// the Chrome theme will be applied (triggering a call to OnThemeColorsChanged
// with |chrome_theme=true|).
views_style::ApplyTo(window_);
window_->ThemeChanged();
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;
}
}
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;
}
cef_state_t ViewsWindow::AcceptsFirstMouse(CefRefPtr<CefWindow> window) {
if (accepts_first_mouse_) {
return STATE_ENABLED;
}
return STATE_DEFAULT;
}
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::OnThemeChanged(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),
use_alloy_style_(delegate->UseAlloyStyle()),
command_line_(command_line) {
DCHECK(delegate_);
if (browser_view) {
SetBrowserView(browser_view);
}
if (MainContext::Get()->UseChromeBootstrap()) {
use_alloy_style_window_ =
use_alloy_style_ &&
!command_line_->HasSwitch(switches::kUseChromeStyleWindow);
} else {
// Alloy bootstrap requires Alloy style.
DCHECK(use_alloy_style_);
use_alloy_style_window_ = true;
}
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);
accepts_first_mouse_ = command_line->HasSwitch(switches::kAcceptsFirstMouse);
// 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(
use_alloy_style_, 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(this);
// 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(this);
// 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