cef/libcef/browser/native/browser_platform_delegate_n...

648 lines
21 KiB
C++
Raw Normal View History

// Copyright 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 "libcef/browser/native/browser_platform_delegate_native_win.h"
#include <shellapi.h>
#include <wininet.h>
#include <winspool.h>
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/native/file_dialog_runner_win.h"
#include "libcef/browser/native/javascript_dialog_runner_win.h"
#include "libcef/browser/native/menu_runner_win.h"
#include "libcef/browser/native/window_delegate_view.h"
#include "libcef/browser/thread_util.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "ui/aura/window.h"
#include "ui/base/win/shell.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion_win.h"
#include "ui/events/keycodes/platform_key_map_win.h"
#include "ui/gfx/win/hwnd_util.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
#include "ui/views/widget/widget.h"
#include "ui/views/win/hwnd_util.h"
#pragma comment(lib, "dwmapi.lib")
namespace {
void WriteTempFileAndView(scoped_refptr<base::RefCountedString> str) {
CEF_REQUIRE_FILET();
base::FilePath tmp_file;
if (!base::CreateTemporaryFile(&tmp_file))
return;
// The shell command will look at the file extension to identify the correct
// program to open.
tmp_file = tmp_file.AddExtension(L"txt");
const std::string& data = str->data();
int write_ct = base::WriteFile(tmp_file, data.c_str(), data.size());
DCHECK_EQ(static_cast<int>(data.size()), write_ct);
ui::win::OpenFileViaShell(tmp_file);
}
// According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
// "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
// ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
// support URLS of 2083 chars in length, 2K is safe."
const int kMaxAddressLengthChars = 2048;
bool HasExternalHandler(const std::string& scheme) {
base::win::RegKey key;
const std::wstring registry_path =
base::ASCIIToUTF16(scheme + "\\shell\\open\\command");
key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ);
if (key.Valid()) {
DWORD size = 0;
key.ReadValue(NULL, NULL, &size, NULL);
if (size > 2) {
// ShellExecute crashes the process when the command is empty.
// We check for "2" because it always returns the trailing NULL.
return true;
}
}
return false;
}
void ExecuteExternalProtocol(const GURL& url) {
CEF_REQUIRE_FILET();
if (!HasExternalHandler(url.scheme()))
return;
const std::string& address = url.spec();
if (address.length() > kMaxAddressLengthChars)
return;
ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
WORD KeyStatesToWord() {
static const USHORT kHighBitMaskShort = 0x8000;
WORD result = 0;
if (GetKeyState(VK_CONTROL) & kHighBitMaskShort)
result |= MK_CONTROL;
if (GetKeyState(VK_SHIFT) & kHighBitMaskShort)
result |= MK_SHIFT;
if (GetKeyState(VK_LBUTTON) & kHighBitMaskShort)
result |= MK_LBUTTON;
if (GetKeyState(VK_MBUTTON) & kHighBitMaskShort)
result |= MK_MBUTTON;
if (GetKeyState(VK_RBUTTON) & kHighBitMaskShort)
result |= MK_RBUTTON;
return result;
}
} // namespace
CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin(
const CefWindowInfo& window_info)
: CefBrowserPlatformDelegateNative(window_info),
host_window_created_(false),
window_widget_(nullptr) {
}
void CefBrowserPlatformDelegateNativeWin::BrowserDestroyed(
CefBrowserHostImpl* browser) {
CefBrowserPlatformDelegate::BrowserDestroyed(browser);
if (host_window_created_) {
// Release the reference added in CreateHostWindow().
browser->Release();
}
}
bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
RegisterWindowClass();
std::wstring windowName(CefString(&window_info_.window_name));
// Create the new browser window.
window_info_.window = CreateWindowEx(window_info_.ex_style,
GetWndClass(), windowName.c_str(), window_info_.style,
window_info_.x, window_info_.y, window_info_.width,
window_info_.height, window_info_.parent_window, window_info_.menu,
::GetModuleHandle(NULL), NULL);
// It's possible for CreateWindowEx to fail if the parent window was
// destroyed between the call to CreateBrowser and the above one.
DCHECK(window_info_.window != NULL);
if (!window_info_.window)
return false;
host_window_created_ = true;
// Set window user data to this object for future reference from the window
// procedure.
gfx::SetWindowUserData(window_info_.window, this);
// Add a reference that will later be released in DestroyBrowser().
browser_->AddRef();
RECT cr;
GetClientRect(window_info_.window, &cr);
DCHECK(!window_widget_);
SkColor background_color = SK_ColorWHITE;
const CefSettings& settings = CefContext::Get()->settings();
if (CefColorGetA(settings.background_color) > 0) {
background_color = SkColorSetRGB(
CefColorGetR(settings.background_color),
CefColorGetG(settings.background_color),
CefColorGetB(settings.background_color));
}
// Adjust for potential display scaling.
gfx::Point point = gfx::Point(cr.right, cr.bottom);
float scale = display::Screen::GetScreen()->
GetDisplayNearestPoint(point).device_scale_factor();
point = gfx::ToFlooredPoint(
gfx::ScalePoint(gfx::PointF(point), 1.0f / scale));
CefWindowDelegateView* delegate_view =
new CefWindowDelegateView(background_color);
delegate_view->Init(window_info_.window,
browser_->web_contents(),
gfx::Rect(0, 0, point.x(), point.y()));
window_widget_ = delegate_view->GetWidget();
window_widget_->Show();
return true;
}
void CefBrowserPlatformDelegateNativeWin::CloseHostWindow() {
if (window_info_.window != NULL) {
HWND frameWnd = GetAncestor(window_info_.window, GA_ROOT);
PostMessage(frameWnd, WM_CLOSE, 0, 0);
}
}
CefWindowHandle
CefBrowserPlatformDelegateNativeWin::GetHostWindowHandle() const {
if (windowless_handler_)
return windowless_handler_->GetParentWindowHandle();
return window_info_.window;
}
views::Widget* CefBrowserPlatformDelegateNativeWin::GetWindowWidget() const {
return window_widget_;
}
void CefBrowserPlatformDelegateNativeWin::SendFocusEvent(bool setFocus) {
if (!setFocus)
return;
if (browser_->web_contents()) {
// Give logical focus to the RenderWidgetHostViewAura in the views
// hierarchy. This does not change the native keyboard focus.
browser_->web_contents()->Focus();
}
if (window_widget_) {
// Give native focus to the DesktopWindowTreeHostWin associated with the
// root window.
//
// The DesktopWindowTreeHostWin HandleNativeFocus/HandleNativeBlur methods
// are called in response to WM_SETFOCUS/WM_KILLFOCUS respectively. The
// implementation has been patched to call HandleActivationChanged which
// results in the following behaviors:
// 1. Update focus/activation state of the aura::Window indirectly via
// wm::FocusController. This allows focus-related behaviors (e.g. focus
// rings, flashing caret, onFocus/onBlur JS events, etc.) to work as
// expected (see issue #1677).
// 2. Update focus state of the ui::InputMethod. If this does not occur
// then InputMethodBase::GetTextInputClient will return NULL and
// InputMethodWin::OnChar will fail to sent character events to the
// renderer (see issue #1700).
//
// This differs from activation in Chrome which is handled via
// HWNDMessageHandler::PostProcessActivateMessage (Widget::Show indirectly
// calls HWNDMessageHandler::Activate which calls ::SetForegroundWindow
// resulting in a WM_ACTIVATE message being sent to the window). The Chrome
// code path doesn't work for CEF because IsTopLevelWindow in
// hwnd_message_handler.cc will return false and consequently
// HWNDMessageHandler::PostProcessActivateMessage will not be called.
//
// Activation events are usually reserved for the top-level window so
// triggering activation based on focus events may be incorrect in some
// circumstances. Revisit this implementation if additional problems are
// discovered.
::SetFocus(HWNDForWidget(window_widget_));
}
}
void CefBrowserPlatformDelegateNativeWin::NotifyMoveOrResizeStarted() {
// Call the parent method to dismiss any existing popups.
CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted();
if (!window_widget_)
return;
// Notify DesktopWindowTreeHostWin of move events so that screen rectangle
// information is communicated to the renderer process and popups are
// displayed in the correct location.
views::DesktopWindowTreeHostWin* tree_host =
static_cast<views::DesktopWindowTreeHostWin*>(
aura::WindowTreeHost::GetForAcceleratedWidget(
HWNDForWidget(window_widget_)));
DCHECK(tree_host);
if (tree_host) {
// Cast to HWNDMessageHandlerDelegate so we can access HandleMove().
static_cast<views::HWNDMessageHandlerDelegate*>(tree_host)->HandleMove();
}
}
void CefBrowserPlatformDelegateNativeWin::SizeTo(int width, int height) {
HWND window = window_info_.window;
RECT rect = {0, 0, width, height};
DWORD style = GetWindowLong(window, GWL_STYLE);
DWORD ex_style = GetWindowLong(window, GWL_EXSTYLE);
bool has_menu = !(style & WS_CHILD) && (GetMenu(window) != 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.
SetWindowPos(window, NULL, 0, 0, rect.right,
rect.bottom, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
}
gfx::Point CefBrowserPlatformDelegateNativeWin::GetScreenPoint(
const gfx::Point& view) const {
if (windowless_handler_)
return windowless_handler_->GetParentScreenPoint(view);
if (!window_widget_)
return view;
aura::Window* window = window_widget_->GetNativeView();
const gfx::Rect& bounds_in_screen = window->GetBoundsInScreen();
const gfx::Point& screen_point = gfx::Point(bounds_in_screen.x() + view.x(),
bounds_in_screen.y() + view.y());
// Adjust for potential display scaling.
float scale = display::Screen::GetScreen()->
GetDisplayNearestPoint(screen_point).device_scale_factor();
return gfx::ToFlooredPoint(
gfx::ScalePoint(gfx::PointF(screen_point), scale));
}
void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) {
std::string str = text;
scoped_refptr<base::RefCountedString> str_ref =
base::RefCountedString::TakeString(&str);
CEF_POST_TASK(CEF_FILET, base::Bind(WriteTempFileAndView, str_ref));
}
void CefBrowserPlatformDelegateNativeWin::HandleKeyboardEvent(
const content::NativeWebKeyboardEvent& event) {
// Any unhandled keyboard/character messages are sent to DefWindowProc so that
// shortcut keys work correctly.
if (event.os_event) {
const MSG& msg = event.os_event->native_event();
DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
} else {
MSG msg = {};
msg.hwnd = GetHostWindowHandle();
if (!msg.hwnd)
return;
switch (event.type) {
case blink::WebInputEvent::RawKeyDown:
msg.message = event.isSystemKey ? WM_SYSKEYDOWN : WM_KEYDOWN;
break;
case blink::WebInputEvent::KeyUp:
msg.message = event.isSystemKey ? WM_SYSKEYUP : WM_KEYUP;
break;
case blink::WebInputEvent::Char:
msg.message = event.isSystemKey ? WM_SYSCHAR: WM_CHAR;
break;
default:
NOTREACHED();
return;
}
msg.wParam = event.windowsKeyCode;
UINT scan_code = ::MapVirtualKeyW(event.windowsKeyCode, MAPVK_VK_TO_VSC);
msg.lParam = (scan_code << 16) | // key scan code
1; // key repeat count
if (event.modifiers & content::NativeWebKeyboardEvent::AltKey)
msg.lParam |= (1 << 29);
DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
}
void CefBrowserPlatformDelegateNativeWin::HandleExternalProtocol(
const GURL& url) {
// Execute on the FILE thread.
CEF_POST_TASK(CEF_FILET,
base::Bind(ExecuteExternalProtocol, url));
}
void CefBrowserPlatformDelegateNativeWin::TranslateKeyEvent(
content::NativeWebKeyboardEvent& result,
const CefKeyEvent& key_event) const {
result.timeStampSeconds = GetMessageTime() / 1000.0;
result.windowsKeyCode = key_event.windows_key_code;
result.nativeKeyCode = key_event.native_key_code;
result.isSystemKey = key_event.is_system_key ? 1 : 0;
switch (key_event.type) {
case KEYEVENT_RAWKEYDOWN:
case KEYEVENT_KEYDOWN:
result.type = blink::WebInputEvent::RawKeyDown;
break;
case KEYEVENT_KEYUP:
result.type = blink::WebInputEvent::KeyUp;
break;
case KEYEVENT_CHAR:
result.type = blink::WebInputEvent::Char;
break;
default:
NOTREACHED();
}
// Populate DOM values that will be passed to JavaScript handlers via
// KeyboardEvent.
result.domCode =
static_cast<int>(ui::KeycodeConverter::NativeKeycodeToDomCode(
key_event.native_key_code));
if (result.type == blink::WebInputEvent::Char) {
result.domKey = ui::DomKey::FromCharacter(key_event.windows_key_code);
} else {
// TODO(cef): CefKeyEvent does not currently pass extended key status (see
// WM_KEYDOWN docs) which would be necessary to pass EF_IS_EXTENDED_KEY as
// the |flags| parameter to DomKeyFromKeyboardCode().
result.domKey = ui::PlatformKeyMap::DomKeyFromKeyboardCode(
ui::KeyboardCodeForWindowsKeyCode(key_event.windows_key_code), 0);
}
if (result.type == blink::WebInputEvent::Char ||
result.type == blink::WebInputEvent::RawKeyDown) {
result.text[0] = result.windowsKeyCode;
result.unmodifiedText[0] = result.windowsKeyCode;
}
result.modifiers |= TranslateModifiers(key_event.modifiers);
}
void CefBrowserPlatformDelegateNativeWin::TranslateClickEvent(
blink::WebMouseEvent& result,
const CefMouseEvent& mouse_event,
CefBrowserHost::MouseButtonType type,
bool mouseUp, int clickCount) const {
TranslateMouseEvent(result, mouse_event);
switch (type) {
case MBT_LEFT:
result.type = mouseUp ? blink::WebInputEvent::MouseUp :
blink::WebInputEvent::MouseDown;
result.button = blink::WebMouseEvent::Button::Left;
break;
case MBT_MIDDLE:
result.type = mouseUp ? blink::WebInputEvent::MouseUp :
blink::WebInputEvent::MouseDown;
result.button = blink::WebMouseEvent::Button::Middle;
break;
case MBT_RIGHT:
result.type = mouseUp ? blink::WebInputEvent::MouseUp :
blink::WebInputEvent::MouseDown;
result.button = blink::WebMouseEvent::Button::Right;
break;
default:
NOTREACHED();
}
result.clickCount = clickCount;
}
void CefBrowserPlatformDelegateNativeWin::TranslateMoveEvent(
blink::WebMouseEvent& result,
const CefMouseEvent& mouse_event,
bool mouseLeave) const {
TranslateMouseEvent(result, mouse_event);
if (!mouseLeave) {
result.type = blink::WebInputEvent::MouseMove;
if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
result.button = blink::WebMouseEvent::Button::Left;
else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
result.button = blink::WebMouseEvent::Button::Middle;
else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
result.button = blink::WebMouseEvent::Button::Right;
else
result.button = blink::WebMouseEvent::Button::NoButton;
} else {
result.type = blink::WebInputEvent::MouseLeave;
result.button = blink::WebMouseEvent::Button::NoButton;
}
result.clickCount = 0;
}
void CefBrowserPlatformDelegateNativeWin::TranslateWheelEvent(
blink::WebMouseWheelEvent& result,
const CefMouseEvent& mouse_event,
int deltaX, int deltaY) const {
TranslateMouseEvent(result, mouse_event);
result.type = blink::WebInputEvent::MouseWheel;
result.button = blink::WebMouseEvent::Button::NoButton;
float wheelDelta;
bool horizontalScroll = false;
wheelDelta = static_cast<float>(deltaY ? deltaY : deltaX);
horizontalScroll = (deltaY == 0);
static const ULONG defaultScrollCharsPerWheelDelta = 1;
static const FLOAT scrollbarPixelsPerLine = 100.0f / 3.0f;
static const ULONG defaultScrollLinesPerWheelDelta = 3;
wheelDelta /= WHEEL_DELTA;
float scrollDelta = wheelDelta;
if (horizontalScroll) {
ULONG scrollChars = defaultScrollCharsPerWheelDelta;
SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
scrollDelta *= static_cast<FLOAT>(scrollChars) * scrollbarPixelsPerLine;
} else {
ULONG scrollLines = defaultScrollLinesPerWheelDelta;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
if (scrollLines == WHEEL_PAGESCROLL)
result.scrollByPage = true;
if (!result.scrollByPage)
scrollDelta *= static_cast<FLOAT>(scrollLines) * scrollbarPixelsPerLine;
}
// Set scroll amount based on above calculations. WebKit expects positive
// deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
if (horizontalScroll) {
result.deltaX = scrollDelta;
result.wheelTicksX = wheelDelta;
} else {
result.deltaY = scrollDelta;
result.wheelTicksY = wheelDelta;
}
}
CefEventHandle CefBrowserPlatformDelegateNativeWin::GetEventHandle(
const content::NativeWebKeyboardEvent& event) const {
if (!event.os_event)
return NULL;
return const_cast<CefEventHandle>(&event.os_event->native_event());
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateNativeWin::CreateFileDialogRunner() {
return base::WrapUnique(new CefFileDialogRunnerWin);
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateNativeWin::CreateJavaScriptDialogRunner() {
return base::WrapUnique(new CefJavaScriptDialogRunnerWin);
}
std::unique_ptr<CefMenuRunner>
CefBrowserPlatformDelegateNativeWin::CreateMenuRunner() {
return base::WrapUnique(new CefMenuRunnerWin);
}
void CefBrowserPlatformDelegateNativeWin::TranslateMouseEvent(
blink::WebMouseEvent& result,
const CefMouseEvent& mouse_event) const {
// position
result.x = mouse_event.x;
result.y = mouse_event.y;
result.windowX = result.x;
result.windowY = result.y;
const gfx::Point& screen_pt = GetScreenPoint(gfx::Point(result.x, result.y));
result.globalX = screen_pt.x();
result.globalY = screen_pt.y();
// modifiers
result.modifiers |= TranslateModifiers(mouse_event.modifiers);
// timestamp
result.timeStampSeconds = GetMessageTime() / 1000.0;
}
// static
void CefBrowserPlatformDelegateNativeWin::RegisterWindowClass() {
static bool registered = false;
if (registered)
return;
// Register the window class
WNDCLASSEX wcex = {
/* cbSize = */ sizeof(WNDCLASSEX),
/* style = */ CS_HREDRAW | CS_VREDRAW,
/* lpfnWndProc = */ CefBrowserPlatformDelegateNativeWin::WndProc,
/* cbClsExtra = */ 0,
/* cbWndExtra = */ 0,
/* hInstance = */ ::GetModuleHandle(NULL),
/* hIcon = */ NULL,
/* hCursor = */ LoadCursor(NULL, IDC_ARROW),
/* hbrBackground = */ 0,
/* lpszMenuName = */ NULL,
/* lpszClassName = */ CefBrowserPlatformDelegateNativeWin::GetWndClass(),
/* hIconSm = */ NULL,
};
RegisterClassEx(&wcex);
registered = true;
}
// static
LPCTSTR CefBrowserPlatformDelegateNativeWin::GetWndClass() {
return L"CefBrowserWindow";
}
// static
LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(
HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) {
CefBrowserPlatformDelegateNativeWin* platform_delegate =
static_cast<CefBrowserPlatformDelegateNativeWin*>(
gfx::GetWindowUserData(hwnd));
CefBrowserHostImpl* browser = nullptr;
if (platform_delegate)
browser = platform_delegate->browser_;
switch (message) {
case WM_CLOSE:
Implement Views framework on Windows and Linux (issue #1749). - Add Views header files in a new include/views directory. - Add initial top-level window (CefWindow), control (CefBrowserView, CefLabelButton, CefMenuButton, CefPanel, CefScrollView, CefTextfield) and layout (CefBoxLayout, CefFlowLayout) support. See libcef/browser/views/view_impl.h comments for implementation details. - Add Views example usage in cefclient and cefsimple and Views unit tests in cef_unittests. Pass the `--use-views` command-line flag to cefclient, cefsimple and cef_unittests to run using the Views framework instead of platform APIs. For cefclient and cefsimple this will create the browser window and all related functionality using the Views framework. For cef_unittests this will run all tests (except OSR tests) in a Views-based browser window. Views- specific unit tests (`--gtest_filter=Views*`) will be run even if the the `--use-views` flag is not specified. - Pass the `--hide-frame` command-line flag to cefclient to demo a frameless Views-based browser window. - Pass the `--hide-controls` command-line flag to cefclient to demo a browser window without top controls. This also works in non-Views mode. - Pass the `--enable-high-dpi-support` command-line flag to cef_unittests on Windows to test high-DPI support on a display that supports it. - Add CefImage for reading/writing image file formats. - Add CefBrowser::DownloadImage() for downloading image URLs as a CefImage representation. This is primarily for loading favicons. - Add CefMenuModel::CreateMenuModel() and CefMenuModelDelegate for creating custom menus. This is primarily for use with CefMenuButton. - Add CefBrowser::TryCloseBrowser() helper for closing a browser. Also improve related documentation in cef_life_span_handler.h. - Rename cef_page_range_t to cef_range_t. It is now also used by CefTextfield. - Remove CefLifeSpanHandler::RunModal() which is never called. - Add draggable regions example to cefclient.
2016-01-19 21:09:01 +01:00
if (browser && !browser->TryCloseBrowser()) {
// Cancel the close.
return 0;
}
// Allow the close.
break;
case WM_DESTROY:
if (platform_delegate) {
// Clear the user data pointer.
gfx::SetWindowUserData(hwnd, NULL);
// Force the browser to be destroyed. This will result in a call to
// BrowserDestroyed() that will release the reference added in
// CreateHostWindow().
browser->WindowDestroyed();
}
return 0;
case WM_SIZE:
if (platform_delegate && platform_delegate->window_widget_) {
// Pass window resize events to the HWND for the DesktopNativeWidgetAura
// root window. Passing size 0x0 (wParam == SIZE_MINIMIZED, for example)
// will cause the widget to be hidden which reduces resource usage.
RECT rc;
GetClientRect(hwnd, &rc);
SetWindowPos(HWNDForWidget(platform_delegate->window_widget_), NULL,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
SWP_NOZORDER);
}
return 0;
case WM_MOVING:
case WM_MOVE:
if (browser)
browser->NotifyMoveOrResizeStarted();
return 0;
case WM_SETFOCUS:
if (browser)
browser->SetFocus(true);
return 0;
case WM_ERASEBKGND:
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}