1060 lines
31 KiB
C++
1060 lines
31 KiB
C++
// Copyright (c) 2015 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/root_window_win.h"
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/base/cef_build.h"
|
|
#include "include/cef_app.h"
|
|
#include "tests/cefclient/browser/browser_window_osr_win.h"
|
|
#include "tests/cefclient/browser/browser_window_std_win.h"
|
|
#include "tests/cefclient/browser/main_context.h"
|
|
#include "tests/cefclient/browser/resource.h"
|
|
#include "tests/cefclient/browser/temp_window.h"
|
|
#include "tests/cefclient/browser/window_test_runner_win.h"
|
|
#include "tests/shared/browser/geometry_util.h"
|
|
#include "tests/shared/browser/main_message_loop.h"
|
|
#include "tests/shared/browser/util_win.h"
|
|
#include "tests/shared/common/client_switches.h"
|
|
|
|
#define MAX_URL_LENGTH 255
|
|
#define BUTTON_WIDTH 72
|
|
#define URLBAR_HEIGHT 24
|
|
|
|
namespace client {
|
|
|
|
namespace {
|
|
|
|
// Message handler for the About box.
|
|
INT_PTR CALLBACK AboutWndProc(HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int GetButtonWidth() {
|
|
static int button_width = BUTTON_WIDTH;
|
|
static bool initialized = false;
|
|
|
|
if (!initialized) {
|
|
button_width =
|
|
LogicalToDevice(BUTTON_WIDTH, client::GetDeviceScaleFactor());
|
|
initialized = true;
|
|
}
|
|
|
|
return button_width;
|
|
}
|
|
|
|
int GetURLBarHeight() {
|
|
static int urlbar_height = URLBAR_HEIGHT;
|
|
static bool initialized = false;
|
|
|
|
if (!initialized) {
|
|
urlbar_height =
|
|
LogicalToDevice(URLBAR_HEIGHT, client::GetDeviceScaleFactor());
|
|
initialized = true;
|
|
}
|
|
|
|
return urlbar_height;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
RootWindowWin::RootWindowWin()
|
|
: with_controls_(false),
|
|
with_osr_(false),
|
|
with_extension_(false),
|
|
is_popup_(false),
|
|
start_rect_(),
|
|
initialized_(false),
|
|
hwnd_(NULL),
|
|
draggable_region_(NULL),
|
|
font_(NULL),
|
|
back_hwnd_(NULL),
|
|
forward_hwnd_(NULL),
|
|
reload_hwnd_(NULL),
|
|
stop_hwnd_(NULL),
|
|
edit_hwnd_(NULL),
|
|
edit_wndproc_old_(NULL),
|
|
find_hwnd_(NULL),
|
|
find_message_id_(0),
|
|
find_wndproc_old_(NULL),
|
|
find_state_(),
|
|
find_next_(false),
|
|
find_match_case_last_(false),
|
|
window_destroyed_(false),
|
|
browser_destroyed_(false) {
|
|
find_buff_[0] = 0;
|
|
|
|
// Create a HRGN representing the draggable window area.
|
|
draggable_region_ = ::CreateRectRgn(0, 0, 0, 0);
|
|
}
|
|
|
|
RootWindowWin::~RootWindowWin() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
::DeleteObject(draggable_region_);
|
|
::DeleteObject(font_);
|
|
|
|
// The window and browser should already have been destroyed.
|
|
DCHECK(window_destroyed_);
|
|
DCHECK(browser_destroyed_);
|
|
}
|
|
|
|
void RootWindowWin::Init(RootWindow::Delegate* delegate,
|
|
const RootWindowConfig& config,
|
|
const CefBrowserSettings& settings) {
|
|
DCHECK(delegate);
|
|
DCHECK(!initialized_);
|
|
|
|
delegate_ = delegate;
|
|
with_controls_ = config.with_controls;
|
|
with_osr_ = config.with_osr;
|
|
with_extension_ = config.with_extension;
|
|
|
|
start_rect_.left = config.bounds.x;
|
|
start_rect_.top = config.bounds.y;
|
|
start_rect_.right = config.bounds.x + config.bounds.width;
|
|
start_rect_.bottom = config.bounds.y + config.bounds.height;
|
|
|
|
CreateBrowserWindow(config.url);
|
|
|
|
initialized_ = true;
|
|
|
|
// Create the native root window on the main thread.
|
|
if (CURRENTLY_ON_MAIN_THREAD()) {
|
|
CreateRootWindow(settings, config.initially_hidden);
|
|
} else {
|
|
MAIN_POST_CLOSURE(base::Bind(&RootWindowWin::CreateRootWindow, this,
|
|
settings, config.initially_hidden));
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::InitAsPopup(RootWindow::Delegate* delegate,
|
|
bool with_controls,
|
|
bool with_osr,
|
|
const CefPopupFeatures& popupFeatures,
|
|
CefWindowInfo& windowInfo,
|
|
CefRefPtr<CefClient>& client,
|
|
CefBrowserSettings& settings) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
DCHECK(delegate);
|
|
DCHECK(!initialized_);
|
|
|
|
delegate_ = delegate;
|
|
with_controls_ = with_controls;
|
|
with_osr_ = with_osr;
|
|
is_popup_ = true;
|
|
|
|
if (popupFeatures.xSet)
|
|
start_rect_.left = popupFeatures.x;
|
|
if (popupFeatures.ySet)
|
|
start_rect_.top = popupFeatures.y;
|
|
if (popupFeatures.widthSet)
|
|
start_rect_.right = start_rect_.left + popupFeatures.width;
|
|
if (popupFeatures.heightSet)
|
|
start_rect_.bottom = start_rect_.top + popupFeatures.height;
|
|
|
|
CreateBrowserWindow(std::string());
|
|
|
|
initialized_ = true;
|
|
|
|
// The new popup is initially parented to a temporary window. The native root
|
|
// window will be created after the browser is created and the popup window
|
|
// will be re-parented to it at that time.
|
|
browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
|
|
client, settings);
|
|
}
|
|
|
|
void RootWindowWin::Show(ShowMode mode) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (!hwnd_)
|
|
return;
|
|
|
|
int nCmdShow = SW_SHOWNORMAL;
|
|
switch (mode) {
|
|
case ShowMinimized:
|
|
nCmdShow = SW_SHOWMINIMIZED;
|
|
break;
|
|
case ShowMaximized:
|
|
nCmdShow = SW_SHOWMAXIMIZED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ShowWindow(hwnd_, nCmdShow);
|
|
UpdateWindow(hwnd_);
|
|
}
|
|
|
|
void RootWindowWin::Hide() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (hwnd_)
|
|
ShowWindow(hwnd_, SW_HIDE);
|
|
}
|
|
|
|
void RootWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (hwnd_) {
|
|
SetWindowPos(hwnd_, NULL, x, y, static_cast<int>(width),
|
|
static_cast<int>(height), SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::Close(bool force) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (hwnd_) {
|
|
if (force)
|
|
DestroyWindow(hwnd_);
|
|
else
|
|
PostMessage(hwnd_, WM_CLOSE, 0, 0);
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (browser_window_ && with_osr_)
|
|
browser_window_->SetDeviceScaleFactor(device_scale_factor);
|
|
}
|
|
|
|
float RootWindowWin::GetDeviceScaleFactor() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (browser_window_ && with_osr_)
|
|
return browser_window_->GetDeviceScaleFactor();
|
|
|
|
NOTREACHED();
|
|
return 0.0f;
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> RootWindowWin::GetBrowser() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (browser_window_)
|
|
return browser_window_->GetBrowser();
|
|
return NULL;
|
|
}
|
|
|
|
ClientWindowHandle RootWindowWin::GetWindowHandle() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
return hwnd_;
|
|
}
|
|
|
|
bool RootWindowWin::WithWindowlessRendering() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
return with_osr_;
|
|
}
|
|
|
|
bool RootWindowWin::WithExtension() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
return with_extension_;
|
|
}
|
|
|
|
void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
|
|
if (with_osr_) {
|
|
OsrRenderer::Settings settings = {};
|
|
MainContext::Get()->PopulateOsrSettings(&settings);
|
|
browser_window_.reset(new BrowserWindowOsrWin(this, startup_url, settings));
|
|
} else {
|
|
browser_window_.reset(new BrowserWindowStdWin(this, startup_url));
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
|
|
bool initially_hidden) {
|
|
REQUIRE_MAIN_THREAD();
|
|
DCHECK(!hwnd_);
|
|
|
|
HINSTANCE hInstance = GetModuleHandle(NULL);
|
|
|
|
// Load strings from the resource file.
|
|
const std::wstring& window_title = GetResourceString(IDS_APP_TITLE);
|
|
const std::wstring& window_class = GetResourceString(IDC_CEFCLIENT);
|
|
|
|
const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
|
|
const HBRUSH background_brush = CreateSolidBrush(
|
|
RGB(CefColorGetR(background_color), CefColorGetG(background_color),
|
|
CefColorGetB(background_color)));
|
|
|
|
// Register the window class.
|
|
RegisterRootClass(hInstance, window_class, background_brush);
|
|
|
|
// Register the message used with the find dialog.
|
|
find_message_id_ = RegisterWindowMessage(FINDMSGSTRING);
|
|
CHECK(find_message_id_);
|
|
|
|
const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
|
|
|
|
int x, y, width, height;
|
|
if (::IsRectEmpty(&start_rect_)) {
|
|
// Use the default window position/size.
|
|
x = y = width = height = CW_USEDEFAULT;
|
|
} else {
|
|
// Adjust the window size to account for window frame and controls.
|
|
RECT window_rect = start_rect_;
|
|
::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, 0);
|
|
if (with_controls_)
|
|
window_rect.bottom += GetURLBarHeight();
|
|
|
|
x = start_rect_.left;
|
|
y = start_rect_.top;
|
|
width = window_rect.right - window_rect.left;
|
|
height = window_rect.bottom - window_rect.top;
|
|
}
|
|
|
|
// Create the main window initially hidden.
|
|
hwnd_ = CreateWindow(window_class.c_str(), window_title.c_str(), dwStyle, x,
|
|
y, width, height, NULL, NULL, hInstance, NULL);
|
|
CHECK(hwnd_);
|
|
|
|
// Associate |this| with the main window.
|
|
SetUserDataPtr(hwnd_, this);
|
|
|
|
RECT rect;
|
|
GetClientRect(hwnd_, &rect);
|
|
|
|
if (with_controls_) {
|
|
// Create the child controls.
|
|
int x_offset = 0;
|
|
|
|
const int button_width = GetButtonWidth();
|
|
const int urlbar_height = GetURLBarHeight();
|
|
const int font_height = LogicalToDevice(14, client::GetDeviceScaleFactor());
|
|
|
|
// Create a scaled font.
|
|
font_ =
|
|
::CreateFont(-font_height, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
|
|
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
|
|
|
|
back_hwnd_ = CreateWindow(
|
|
L"BUTTON", L"Back", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
|
x_offset, 0, button_width, urlbar_height, hwnd_,
|
|
reinterpret_cast<HMENU>(IDC_NAV_BACK), hInstance, 0);
|
|
CHECK(back_hwnd_);
|
|
SendMessage(back_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_), TRUE);
|
|
x_offset += button_width;
|
|
|
|
forward_hwnd_ =
|
|
CreateWindow(L"BUTTON", L"Forward",
|
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
|
x_offset, 0, button_width, urlbar_height, hwnd_,
|
|
reinterpret_cast<HMENU>(IDC_NAV_FORWARD), hInstance, 0);
|
|
CHECK(forward_hwnd_);
|
|
SendMessage(forward_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
|
|
TRUE);
|
|
x_offset += button_width;
|
|
|
|
reload_hwnd_ =
|
|
CreateWindow(L"BUTTON", L"Reload",
|
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
|
x_offset, 0, button_width, urlbar_height, hwnd_,
|
|
reinterpret_cast<HMENU>(IDC_NAV_RELOAD), hInstance, 0);
|
|
CHECK(reload_hwnd_);
|
|
SendMessage(reload_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
|
|
TRUE);
|
|
x_offset += button_width;
|
|
|
|
stop_hwnd_ = CreateWindow(
|
|
L"BUTTON", L"Stop", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
|
x_offset, 0, button_width, urlbar_height, hwnd_,
|
|
reinterpret_cast<HMENU>(IDC_NAV_STOP), hInstance, 0);
|
|
CHECK(stop_hwnd_);
|
|
SendMessage(stop_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_), TRUE);
|
|
x_offset += button_width;
|
|
|
|
edit_hwnd_ = CreateWindow(L"EDIT", 0,
|
|
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
|
|
ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_DISABLED,
|
|
x_offset, 0, rect.right - button_width * 4,
|
|
urlbar_height, hwnd_, 0, hInstance, 0);
|
|
SendMessage(edit_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_), TRUE);
|
|
CHECK(edit_hwnd_);
|
|
|
|
// Override the edit control's window procedure.
|
|
edit_wndproc_old_ = SetWndProcPtr(edit_hwnd_, EditWndProc);
|
|
|
|
// Associate |this| with the edit window.
|
|
SetUserDataPtr(edit_hwnd_, this);
|
|
|
|
rect.top += urlbar_height;
|
|
|
|
if (!with_osr_) {
|
|
// Remove the menu items that are only used with OSR.
|
|
HMENU hMenu = ::GetMenu(hwnd_);
|
|
if (hMenu) {
|
|
HMENU hTestMenu = ::GetSubMenu(hMenu, 2);
|
|
if (hTestMenu) {
|
|
::RemoveMenu(hTestMenu, ID_TESTS_OSR_FPS, MF_BYCOMMAND);
|
|
::RemoveMenu(hTestMenu, ID_TESTS_OSR_DSF, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// No controls so also remove the default menu.
|
|
::SetMenu(hwnd_, NULL);
|
|
}
|
|
|
|
if (!is_popup_) {
|
|
// Create the browser window.
|
|
CefRect cef_rect(rect.left, rect.top, rect.right - rect.left,
|
|
rect.bottom - rect.top);
|
|
browser_window_->CreateBrowser(hwnd_, cef_rect, settings,
|
|
delegate_->GetRequestContext(this));
|
|
} else {
|
|
// With popups we already have a browser window. Parent the browser window
|
|
// to the root window and show it in the correct location.
|
|
browser_window_->ShowPopup(hwnd_, rect.left, rect.top,
|
|
rect.right - rect.left, rect.bottom - rect.top);
|
|
}
|
|
|
|
if (!initially_hidden) {
|
|
// Show this window.
|
|
Show(ShowNormal);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void RootWindowWin::RegisterRootClass(HINSTANCE hInstance,
|
|
const std::wstring& window_class,
|
|
HBRUSH background_brush) {
|
|
// Only register the class one time.
|
|
static bool class_registered = false;
|
|
if (class_registered)
|
|
return;
|
|
class_registered = true;
|
|
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = RootWndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CEFCLIENT));
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = background_brush;
|
|
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CEFCLIENT);
|
|
wcex.lpszClassName = window_class.c_str();
|
|
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
|
|
|
|
RegisterClassEx(&wcex);
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK RootWindowWin::EditWndProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
|
|
DCHECK(self);
|
|
DCHECK(hWnd == self->edit_hwnd_);
|
|
|
|
switch (message) {
|
|
case WM_CHAR:
|
|
if (wParam == VK_RETURN) {
|
|
// When the user hits the enter key load the URL.
|
|
CefRefPtr<CefBrowser> browser = self->GetBrowser();
|
|
if (browser) {
|
|
wchar_t strPtr[MAX_URL_LENGTH + 1] = {0};
|
|
*((LPWORD)strPtr) = MAX_URL_LENGTH;
|
|
LRESULT strLen = SendMessage(hWnd, EM_GETLINE, 0, (LPARAM)strPtr);
|
|
if (strLen > 0) {
|
|
strPtr[strLen] = 0;
|
|
browser->GetMainFrame()->LoadURL(strPtr);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_NCDESTROY:
|
|
// Clear the reference to |self|.
|
|
SetUserDataPtr(hWnd, NULL);
|
|
self->edit_hwnd_ = NULL;
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(self->edit_wndproc_old_, hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK RootWindowWin::FindWndProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
|
|
DCHECK(self);
|
|
DCHECK(hWnd == self->find_hwnd_);
|
|
|
|
switch (message) {
|
|
case WM_ACTIVATE:
|
|
// Set this dialog as current when activated.
|
|
MainMessageLoop::Get()->SetCurrentModelessDialog(wParam == 0 ? NULL
|
|
: hWnd);
|
|
return FALSE;
|
|
case WM_NCDESTROY:
|
|
// Clear the reference to |self|.
|
|
SetUserDataPtr(hWnd, NULL);
|
|
self->find_hwnd_ = NULL;
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(self->find_wndproc_old_, hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
|
|
if (!self)
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
DCHECK(hWnd == self->hwnd_);
|
|
|
|
if (message == self->find_message_id_) {
|
|
// Message targeting the find dialog.
|
|
LPFINDREPLACE lpfr = reinterpret_cast<LPFINDREPLACE>(lParam);
|
|
CHECK(lpfr == &self->find_state_);
|
|
self->OnFindEvent();
|
|
return 0;
|
|
}
|
|
|
|
// Callback for the main window
|
|
switch (message) {
|
|
case WM_COMMAND:
|
|
if (self->OnCommand(LOWORD(wParam)))
|
|
return 0;
|
|
break;
|
|
|
|
case WM_GETOBJECT: {
|
|
// Only the lower 32 bits of lParam are valid when checking the object id
|
|
// because it sometimes gets sign-extended incorrectly (but not always).
|
|
DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
|
|
|
|
// Accessibility readers will send an OBJID_CLIENT message.
|
|
if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
|
|
if (self->GetBrowser() && self->GetBrowser()->GetHost())
|
|
self->GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
|
|
}
|
|
} break;
|
|
|
|
case WM_PAINT:
|
|
self->OnPaint();
|
|
return 0;
|
|
|
|
case WM_ACTIVATE:
|
|
self->OnActivate(LOWORD(wParam) != WA_INACTIVE);
|
|
// Allow DefWindowProc to set keyboard focus.
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
self->OnFocus();
|
|
return 0;
|
|
|
|
case WM_SIZE:
|
|
self->OnSize(wParam == SIZE_MINIMIZED);
|
|
break;
|
|
|
|
case WM_MOVING:
|
|
case WM_MOVE:
|
|
self->OnMove();
|
|
return 0;
|
|
|
|
case WM_ERASEBKGND:
|
|
if (self->OnEraseBkgnd())
|
|
break;
|
|
// Don't erase the background.
|
|
return 0;
|
|
|
|
case WM_ENTERMENULOOP:
|
|
if (!wParam) {
|
|
// Entering the menu loop for the application menu.
|
|
CefSetOSModalLoop(true);
|
|
}
|
|
break;
|
|
|
|
case WM_EXITMENULOOP:
|
|
if (!wParam) {
|
|
// Exiting the menu loop for the application menu.
|
|
CefSetOSModalLoop(false);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
if (self->OnClose())
|
|
return 0; // Cancel the close.
|
|
break;
|
|
|
|
case WM_NCHITTEST: {
|
|
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
|
|
if (hit == HTCLIENT) {
|
|
POINTS points = MAKEPOINTS(lParam);
|
|
POINT point = {points.x, points.y};
|
|
::ScreenToClient(hWnd, &point);
|
|
if (::PtInRegion(self->draggable_region_, point.x, point.y)) {
|
|
// If cursor is inside a draggable region return HTCAPTION to allow
|
|
// dragging.
|
|
return HTCAPTION;
|
|
}
|
|
}
|
|
return hit;
|
|
}
|
|
|
|
case WM_NCDESTROY:
|
|
// Clear the reference to |self|.
|
|
SetUserDataPtr(hWnd, NULL);
|
|
self->hwnd_ = NULL;
|
|
self->OnDestroyed();
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
void RootWindowWin::OnPaint() {
|
|
PAINTSTRUCT ps;
|
|
BeginPaint(hwnd_, &ps);
|
|
EndPaint(hwnd_, &ps);
|
|
}
|
|
|
|
void RootWindowWin::OnFocus() {
|
|
if (browser_window_)
|
|
browser_window_->SetFocus(true);
|
|
}
|
|
|
|
void RootWindowWin::OnActivate(bool active) {
|
|
if (active)
|
|
delegate_->OnRootWindowActivated(this);
|
|
}
|
|
|
|
void RootWindowWin::OnSize(bool minimized) {
|
|
if (minimized) {
|
|
// Notify the browser window that it was hidden and do nothing further.
|
|
if (browser_window_)
|
|
browser_window_->Hide();
|
|
return;
|
|
}
|
|
|
|
if (browser_window_)
|
|
browser_window_->Show();
|
|
|
|
RECT rect;
|
|
GetClientRect(hwnd_, &rect);
|
|
|
|
if (with_controls_) {
|
|
static int button_width = GetButtonWidth();
|
|
static int urlbar_height = GetURLBarHeight();
|
|
|
|
// Resize the window and address bar to match the new frame size.
|
|
rect.top += urlbar_height;
|
|
|
|
int urloffset = rect.left + button_width * 4;
|
|
|
|
// |browser_hwnd| may be NULL if the browser has not yet been created.
|
|
HWND browser_hwnd = NULL;
|
|
if (browser_window_)
|
|
browser_hwnd = browser_window_->GetWindowHandle();
|
|
|
|
if (browser_hwnd) {
|
|
// Resize both the browser and the URL edit field.
|
|
HDWP hdwp = BeginDeferWindowPos(2);
|
|
hdwp =
|
|
DeferWindowPos(hdwp, edit_hwnd_, NULL, urloffset, 0,
|
|
rect.right - urloffset, urlbar_height, SWP_NOZORDER);
|
|
hdwp = DeferWindowPos(hdwp, browser_hwnd, NULL, rect.left, rect.top,
|
|
rect.right - rect.left, rect.bottom - rect.top,
|
|
SWP_NOZORDER);
|
|
BOOL result = EndDeferWindowPos(hdwp);
|
|
ALLOW_UNUSED_LOCAL(result);
|
|
DCHECK(result);
|
|
} else {
|
|
// Resize just the URL edit field.
|
|
SetWindowPos(edit_hwnd_, NULL, urloffset, 0, rect.right - urloffset,
|
|
urlbar_height, SWP_NOZORDER);
|
|
}
|
|
} else if (browser_window_) {
|
|
// Size the browser window to the whole client area.
|
|
browser_window_->SetBounds(0, 0, rect.right, rect.bottom);
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::OnMove() {
|
|
// Notify the browser of move events so that popup windows are displayed
|
|
// in the correct location and dismissed when the window moves.
|
|
CefRefPtr<CefBrowser> browser = GetBrowser();
|
|
if (browser)
|
|
browser->GetHost()->NotifyMoveOrResizeStarted();
|
|
}
|
|
|
|
bool RootWindowWin::OnEraseBkgnd() {
|
|
// Erase the background when the browser does not exist.
|
|
return (GetBrowser() == NULL);
|
|
}
|
|
|
|
bool RootWindowWin::OnCommand(UINT id) {
|
|
if (id >= ID_TESTS_FIRST && id <= ID_TESTS_LAST) {
|
|
delegate_->OnTest(this, id);
|
|
return true;
|
|
}
|
|
|
|
switch (id) {
|
|
case IDM_ABOUT:
|
|
OnAbout();
|
|
return true;
|
|
case IDM_EXIT:
|
|
delegate_->OnExit(this);
|
|
return true;
|
|
case ID_FIND:
|
|
OnFind();
|
|
return true;
|
|
case IDC_NAV_BACK: // Back button
|
|
if (CefRefPtr<CefBrowser> browser = GetBrowser())
|
|
browser->GoBack();
|
|
return true;
|
|
case IDC_NAV_FORWARD: // Forward button
|
|
if (CefRefPtr<CefBrowser> browser = GetBrowser())
|
|
browser->GoForward();
|
|
return true;
|
|
case IDC_NAV_RELOAD: // Reload button
|
|
if (CefRefPtr<CefBrowser> browser = GetBrowser())
|
|
browser->Reload();
|
|
return true;
|
|
case IDC_NAV_STOP: // Stop button
|
|
if (CefRefPtr<CefBrowser> browser = GetBrowser())
|
|
browser->StopLoad();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RootWindowWin::OnFind() {
|
|
if (find_hwnd_) {
|
|
// Give focus to the existing find dialog.
|
|
::SetFocus(find_hwnd_);
|
|
return;
|
|
}
|
|
|
|
// Configure dialog state.
|
|
ZeroMemory(&find_state_, sizeof(find_state_));
|
|
find_state_.lStructSize = sizeof(find_state_);
|
|
find_state_.hwndOwner = hwnd_;
|
|
find_state_.lpstrFindWhat = find_buff_;
|
|
find_state_.wFindWhatLen = sizeof(find_buff_);
|
|
find_state_.Flags = FR_HIDEWHOLEWORD | FR_DOWN;
|
|
|
|
// Create the dialog.
|
|
find_hwnd_ = FindText(&find_state_);
|
|
|
|
// Override the dialog's window procedure.
|
|
find_wndproc_old_ = SetWndProcPtr(find_hwnd_, FindWndProc);
|
|
|
|
// Associate |self| with the dialog.
|
|
SetUserDataPtr(find_hwnd_, this);
|
|
}
|
|
|
|
void RootWindowWin::OnFindEvent() {
|
|
CefRefPtr<CefBrowser> browser = GetBrowser();
|
|
|
|
if (find_state_.Flags & FR_DIALOGTERM) {
|
|
// The find dialog box has been dismissed so invalidate the handle and
|
|
// reset the search results.
|
|
if (browser) {
|
|
browser->GetHost()->StopFinding(true);
|
|
find_what_last_.clear();
|
|
find_next_ = false;
|
|
}
|
|
} else if ((find_state_.Flags & FR_FINDNEXT) && browser) {
|
|
// Search for the requested string.
|
|
bool match_case = ((find_state_.Flags & FR_MATCHCASE) ? true : false);
|
|
const std::wstring& find_what = find_buff_;
|
|
if (match_case != find_match_case_last_ || find_what != find_what_last_) {
|
|
// The search string has changed, so reset the search results.
|
|
if (!find_what.empty()) {
|
|
browser->GetHost()->StopFinding(true);
|
|
find_next_ = false;
|
|
}
|
|
find_match_case_last_ = match_case;
|
|
find_what_last_ = find_buff_;
|
|
}
|
|
|
|
browser->GetHost()->Find(0, find_what,
|
|
(find_state_.Flags & FR_DOWN) ? true : false,
|
|
match_case, find_next_);
|
|
if (!find_next_)
|
|
find_next_ = true;
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::OnAbout() {
|
|
// Show the about box.
|
|
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
|
|
AboutWndProc);
|
|
}
|
|
|
|
bool RootWindowWin::OnClose() {
|
|
if (browser_window_ && !browser_window_->IsClosing()) {
|
|
CefRefPtr<CefBrowser> browser = GetBrowser();
|
|
if (browser) {
|
|
// Notify the browser window that we would like to close it. This
|
|
// will result in a call to ClientHandler::DoClose() if the
|
|
// JavaScript 'onbeforeunload' event handler allows it.
|
|
browser->GetHost()->CloseBrowser(false);
|
|
|
|
// Cancel the close.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Allow the close.
|
|
return false;
|
|
}
|
|
|
|
void RootWindowWin::OnDestroyed() {
|
|
window_destroyed_ = true;
|
|
NotifyDestroyedIfDone();
|
|
}
|
|
|
|
void RootWindowWin::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (is_popup_) {
|
|
// For popup browsers create the root window once the browser has been
|
|
// created.
|
|
CreateRootWindow(CefBrowserSettings(), false);
|
|
} else {
|
|
// Make sure the browser is sized correctly.
|
|
OnSize(false);
|
|
}
|
|
|
|
delegate_->OnBrowserCreated(this, browser);
|
|
}
|
|
|
|
void RootWindowWin::OnBrowserWindowDestroyed() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
browser_window_.reset();
|
|
|
|
if (!window_destroyed_) {
|
|
// The browser was destroyed first. This could be due to the use of
|
|
// off-screen rendering or execution of JavaScript window.close().
|
|
// Close the RootWindow.
|
|
Close(true);
|
|
}
|
|
|
|
browser_destroyed_ = true;
|
|
NotifyDestroyedIfDone();
|
|
}
|
|
|
|
void RootWindowWin::OnSetAddress(const std::string& url) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (edit_hwnd_)
|
|
SetWindowText(edit_hwnd_, CefString(url).ToWString().c_str());
|
|
}
|
|
|
|
void RootWindowWin::OnSetTitle(const std::string& title) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (hwnd_)
|
|
SetWindowText(hwnd_, CefString(title).ToWString().c_str());
|
|
}
|
|
|
|
void RootWindowWin::OnSetFullscreen(bool fullscreen) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
CefRefPtr<CefBrowser> browser = GetBrowser();
|
|
if (browser) {
|
|
scoped_ptr<window_test::WindowTestRunnerWin> test_runner(
|
|
new window_test::WindowTestRunnerWin());
|
|
if (fullscreen)
|
|
test_runner->Maximize(browser);
|
|
else
|
|
test_runner->Restore(browser);
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::OnAutoResize(const CefSize& new_size) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (!hwnd_)
|
|
return;
|
|
|
|
int new_width = new_size.width;
|
|
|
|
// Make the window wide enough to drag by the top menu bar.
|
|
if (new_width < 200)
|
|
new_width = 200;
|
|
|
|
RECT rect = {
|
|
0, 0, LogicalToDevice(new_width, client::GetDeviceScaleFactor()),
|
|
LogicalToDevice(new_size.height, client::GetDeviceScaleFactor())};
|
|
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
|
|
DWORD ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
|
|
bool has_menu = !(style & WS_CHILD) && (GetMenu(hwnd_) != NULL);
|
|
|
|
// The size value is for the client area. Calculate the whole window size
|
|
// based on the current style.
|
|
AdjustWindowRectEx(&rect, style, has_menu, ex_style);
|
|
|
|
// Size the window. The left/top values may be negative.
|
|
// Also show the window if it's not currently visible.
|
|
SetWindowPos(hwnd_, NULL, 0, 0, rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
|
}
|
|
|
|
void RootWindowWin::OnSetLoadingState(bool isLoading,
|
|
bool canGoBack,
|
|
bool canGoForward) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (with_controls_) {
|
|
EnableWindow(back_hwnd_, canGoBack);
|
|
EnableWindow(forward_hwnd_, canGoForward);
|
|
EnableWindow(reload_hwnd_, !isLoading);
|
|
EnableWindow(stop_hwnd_, isLoading);
|
|
EnableWindow(edit_hwnd_, TRUE);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
LPCWSTR kParentWndProc = L"CefParentWndProc";
|
|
LPCWSTR kDraggableRegion = L"CefDraggableRegion";
|
|
|
|
LRESULT CALLBACK SubclassedWindowProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
WNDPROC hParentWndProc =
|
|
reinterpret_cast<WNDPROC>(::GetPropW(hWnd, kParentWndProc));
|
|
HRGN hRegion = reinterpret_cast<HRGN>(::GetPropW(hWnd, kDraggableRegion));
|
|
|
|
if (message == WM_NCHITTEST) {
|
|
LRESULT hit = CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
|
|
if (hit == HTCLIENT) {
|
|
POINTS points = MAKEPOINTS(lParam);
|
|
POINT point = {points.x, points.y};
|
|
::ScreenToClient(hWnd, &point);
|
|
if (::PtInRegion(hRegion, point.x, point.y)) {
|
|
// Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT
|
|
// in child windows.
|
|
return HTTRANSPARENT;
|
|
}
|
|
}
|
|
return hit;
|
|
}
|
|
|
|
return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
void SubclassWindow(HWND hWnd, HRGN hRegion) {
|
|
HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc);
|
|
if (hParentWndProc) {
|
|
return;
|
|
}
|
|
|
|
SetLastError(0);
|
|
LONG_PTR hOldWndProc = SetWindowLongPtr(
|
|
hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
|
|
if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
::SetPropW(hWnd, kParentWndProc, reinterpret_cast<HANDLE>(hOldWndProc));
|
|
::SetPropW(hWnd, kDraggableRegion, reinterpret_cast<HANDLE>(hRegion));
|
|
}
|
|
|
|
void UnSubclassWindow(HWND hWnd) {
|
|
LONG_PTR hParentWndProc =
|
|
reinterpret_cast<LONG_PTR>(::GetPropW(hWnd, kParentWndProc));
|
|
if (hParentWndProc) {
|
|
LONG_PTR hPreviousWndProc =
|
|
SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
|
|
ALLOW_UNUSED_LOCAL(hPreviousWndProc);
|
|
DCHECK_EQ(hPreviousWndProc,
|
|
reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
|
|
}
|
|
|
|
::RemovePropW(hWnd, kParentWndProc);
|
|
::RemovePropW(hWnd, kDraggableRegion);
|
|
}
|
|
|
|
BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) {
|
|
SubclassWindow(hwnd, reinterpret_cast<HRGN>(lParam));
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) {
|
|
UnSubclassWindow(hwnd);
|
|
return TRUE;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void RootWindowWin::OnSetDraggableRegions(
|
|
const std::vector<CefDraggableRegion>& regions) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
// Reset draggable region.
|
|
::SetRectRgn(draggable_region_, 0, 0, 0, 0);
|
|
|
|
// Determine new draggable region.
|
|
std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
|
|
for (; it != regions.end(); ++it) {
|
|
HRGN region = ::CreateRectRgn(it->bounds.x, it->bounds.y,
|
|
it->bounds.x + it->bounds.width,
|
|
it->bounds.y + it->bounds.height);
|
|
::CombineRgn(draggable_region_, draggable_region_, region,
|
|
it->draggable ? RGN_OR : RGN_DIFF);
|
|
::DeleteObject(region);
|
|
}
|
|
|
|
// Subclass child window procedures in order to do hit-testing.
|
|
// This will be a no-op, if it is already subclassed.
|
|
if (hwnd_) {
|
|
WNDENUMPROC proc =
|
|
!regions.empty() ? SubclassWindowsProc : UnSubclassWindowsProc;
|
|
::EnumChildWindows(hwnd_, proc,
|
|
reinterpret_cast<LPARAM>(draggable_region_));
|
|
}
|
|
}
|
|
|
|
void RootWindowWin::NotifyDestroyedIfDone() {
|
|
// Notify once both the window and the browser have been destroyed.
|
|
if (window_destroyed_ && browser_destroyed_)
|
|
delegate_->OnRootWindowDestroyed(this);
|
|
}
|
|
|
|
} // namespace client
|