blob: 39912ceaf9de823c003fdce36493e269ffec2cd1 [file] [log] [blame]
// 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/main_message_loop_multithreaded_win.h"
#include "include/base/cef_bind.h"
#include "include/base/cef_logging.h"
#include "include/cef_app.h"
#include "tests/cefclient/browser/resource.h"
#include "tests/shared/browser/util_win.h"
namespace client {
namespace {
const wchar_t kWndClass[] = L"Client_MessageWindow";
const wchar_t kTaskMessageName[] = L"Client_CustomTask";
} // namespace
MainMessageLoopMultithreadedWin::MainMessageLoopMultithreadedWin()
: thread_id_(base::PlatformThread::CurrentId()),
task_message_id_(RegisterWindowMessage(kTaskMessageName)),
dialog_hwnd_(NULL),
message_hwnd_(NULL) {}
MainMessageLoopMultithreadedWin::~MainMessageLoopMultithreadedWin() {
DCHECK(RunsTasksOnCurrentThread());
DCHECK(!message_hwnd_);
DCHECK(queued_tasks_.empty());
}
int MainMessageLoopMultithreadedWin::Run() {
DCHECK(RunsTasksOnCurrentThread());
HINSTANCE hInstance = ::GetModuleHandle(NULL);
{
base::AutoLock lock_scope(lock_);
// Create the hidden window for message processing.
message_hwnd_ = CreateMessageWindow(hInstance);
CHECK(message_hwnd_);
// Store a pointer to |this| in the window's user data.
SetUserDataPtr(message_hwnd_, this);
// Execute any tasks that are currently queued.
while (!queued_tasks_.empty()) {
PostTaskInternal(queued_tasks_.front());
queued_tasks_.pop();
}
}
HACCEL hAccelTable =
LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));
MSG msg;
// Run the application message loop.
while (GetMessage(&msg, NULL, 0, 0)) {
// Allow processing of dialog messages.
if (dialog_hwnd_ && IsDialogMessage(dialog_hwnd_, &msg))
continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
{
base::AutoLock lock_scope(lock_);
// Destroy the message window.
DestroyWindow(message_hwnd_);
message_hwnd_ = NULL;
}
return static_cast<int>(msg.wParam);
}
void MainMessageLoopMultithreadedWin::Quit() {
// Execute PostQuitMessage(0) on the main thread.
PostClosure(base::Bind(::PostQuitMessage, 0));
}
void MainMessageLoopMultithreadedWin::PostTask(CefRefPtr<CefTask> task) {
base::AutoLock lock_scope(lock_);
PostTaskInternal(task);
}
bool MainMessageLoopMultithreadedWin::RunsTasksOnCurrentThread() const {
return (thread_id_ == base::PlatformThread::CurrentId());
}
void MainMessageLoopMultithreadedWin::SetCurrentModelessDialog(
HWND hWndDialog) {
DCHECK(RunsTasksOnCurrentThread());
#if DCHECK_IS_ON()
if (hWndDialog) {
// A new dialog reference should not be set while one is currently set.
DCHECK(!dialog_hwnd_);
}
#endif
dialog_hwnd_ = hWndDialog;
}
// static
HWND MainMessageLoopMultithreadedWin::CreateMessageWindow(HINSTANCE hInstance) {
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = MessageWndProc;
wc.hInstance = hInstance;
wc.lpszClassName = kWndClass;
RegisterClassEx(&wc);
return CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance,
0);
}
// static
LRESULT CALLBACK
MainMessageLoopMultithreadedWin::MessageWndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam) {
MainMessageLoopMultithreadedWin* self =
GetUserDataPtr<MainMessageLoopMultithreadedWin*>(hWnd);
if (self && message == self->task_message_id_) {
// Execute the task.
CefTask* task = reinterpret_cast<CefTask*>(wParam);
task->Execute();
// Release the reference added in PostTaskInternal. This will likely result
// in |task| being deleted.
task->Release();
} else {
switch (message) {
case WM_NCDESTROY:
// Clear the reference to |self|.
SetUserDataPtr(hWnd, NULL);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
void MainMessageLoopMultithreadedWin::PostTaskInternal(
CefRefPtr<CefTask> task) {
lock_.AssertAcquired();
if (!message_hwnd_) {
// Queue the task until the message loop starts running.
queued_tasks_.push(task);
return;
}
// Add a reference that will be released in MessageWndProc.
task->AddRef();
// Post the task for execution by the message window.
PostMessage(message_hwnd_, task_message_id_,
reinterpret_cast<WPARAM>(task.get()), 0);
}
} // namespace client