1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/main_message_loop_multithreaded_win.h"
6
7 #include "include/base/cef_callback.h"
8 #include "include/base/cef_logging.h"
9 #include "include/cef_app.h"
10 #include "tests/cefclient/browser/resource.h"
11 #include "tests/shared/browser/util_win.h"
12
13 namespace client {
14
15 namespace {
16
17 const wchar_t kWndClass[] = L"Client_MessageWindow";
18 const wchar_t kTaskMessageName[] = L"Client_CustomTask";
19
20 } // namespace
21
MainMessageLoopMultithreadedWin()22 MainMessageLoopMultithreadedWin::MainMessageLoopMultithreadedWin()
23 : thread_id_(base::PlatformThread::CurrentId()),
24 task_message_id_(RegisterWindowMessage(kTaskMessageName)),
25 dialog_hwnd_(nullptr),
26 message_hwnd_(nullptr) {}
27
~MainMessageLoopMultithreadedWin()28 MainMessageLoopMultithreadedWin::~MainMessageLoopMultithreadedWin() {
29 DCHECK(RunsTasksOnCurrentThread());
30 DCHECK(!message_hwnd_);
31 DCHECK(queued_tasks_.empty());
32 }
33
Run()34 int MainMessageLoopMultithreadedWin::Run() {
35 DCHECK(RunsTasksOnCurrentThread());
36
37 HINSTANCE hInstance = ::GetModuleHandle(nullptr);
38
39 {
40 base::AutoLock lock_scope(lock_);
41
42 // Create the hidden window for message processing.
43 message_hwnd_ = CreateMessageWindow(hInstance);
44 CHECK(message_hwnd_);
45
46 // Store a pointer to |this| in the window's user data.
47 SetUserDataPtr(message_hwnd_, this);
48
49 // Execute any tasks that are currently queued.
50 while (!queued_tasks_.empty()) {
51 PostTaskInternal(queued_tasks_.front());
52 queued_tasks_.pop();
53 }
54 }
55
56 HACCEL hAccelTable =
57 LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));
58
59 MSG msg;
60
61 // Run the application message loop.
62 while (GetMessage(&msg, nullptr, 0, 0)) {
63 // Allow processing of dialog messages.
64 if (dialog_hwnd_ && IsDialogMessage(dialog_hwnd_, &msg))
65 continue;
66
67 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
68 TranslateMessage(&msg);
69 DispatchMessage(&msg);
70 }
71 }
72
73 {
74 base::AutoLock lock_scope(lock_);
75
76 // Destroy the message window.
77 DestroyWindow(message_hwnd_);
78 message_hwnd_ = nullptr;
79 }
80
81 return static_cast<int>(msg.wParam);
82 }
83
Quit()84 void MainMessageLoopMultithreadedWin::Quit() {
85 // Execute PostQuitMessage(0) on the main thread.
86 PostClosure(base::BindOnce(::PostQuitMessage, 0));
87 }
88
PostTask(CefRefPtr<CefTask> task)89 void MainMessageLoopMultithreadedWin::PostTask(CefRefPtr<CefTask> task) {
90 base::AutoLock lock_scope(lock_);
91 PostTaskInternal(task);
92 }
93
RunsTasksOnCurrentThread() const94 bool MainMessageLoopMultithreadedWin::RunsTasksOnCurrentThread() const {
95 return (thread_id_ == base::PlatformThread::CurrentId());
96 }
97
SetCurrentModelessDialog(HWND hWndDialog)98 void MainMessageLoopMultithreadedWin::SetCurrentModelessDialog(
99 HWND hWndDialog) {
100 DCHECK(RunsTasksOnCurrentThread());
101
102 #if DCHECK_IS_ON()
103 if (hWndDialog) {
104 // A new dialog reference should not be set while one is currently set.
105 DCHECK(!dialog_hwnd_);
106 }
107 #endif
108 dialog_hwnd_ = hWndDialog;
109 }
110
111 // static
CreateMessageWindow(HINSTANCE hInstance)112 HWND MainMessageLoopMultithreadedWin::CreateMessageWindow(HINSTANCE hInstance) {
113 WNDCLASSEX wc = {0};
114 wc.cbSize = sizeof(wc);
115 wc.lpfnWndProc = MessageWndProc;
116 wc.hInstance = hInstance;
117 wc.lpszClassName = kWndClass;
118 RegisterClassEx(&wc);
119
120 return CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance,
121 0);
122 }
123
124 // static
125 LRESULT CALLBACK
MessageWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)126 MainMessageLoopMultithreadedWin::MessageWndProc(HWND hWnd,
127 UINT message,
128 WPARAM wParam,
129 LPARAM lParam) {
130 MainMessageLoopMultithreadedWin* self =
131 GetUserDataPtr<MainMessageLoopMultithreadedWin*>(hWnd);
132
133 if (self && message == self->task_message_id_) {
134 // Execute the task.
135 CefTask* task = reinterpret_cast<CefTask*>(wParam);
136 task->Execute();
137
138 // Release the reference added in PostTaskInternal. This will likely result
139 // in |task| being deleted.
140 task->Release();
141 } else {
142 switch (message) {
143 case WM_NCDESTROY:
144 // Clear the reference to |self|.
145 SetUserDataPtr(hWnd, nullptr);
146 break;
147 }
148 }
149
150 return DefWindowProc(hWnd, message, wParam, lParam);
151 }
152
PostTaskInternal(CefRefPtr<CefTask> task)153 void MainMessageLoopMultithreadedWin::PostTaskInternal(
154 CefRefPtr<CefTask> task) {
155 lock_.AssertAcquired();
156
157 if (!message_hwnd_) {
158 // Queue the task until the message loop starts running.
159 queued_tasks_.push(task);
160 return;
161 }
162
163 // Add a reference that will be released in MessageWndProc.
164 task->AddRef();
165
166 // Post the task for execution by the message window.
167 PostMessage(message_hwnd_, task_message_id_,
168 reinterpret_cast<WPARAM>(task.get()), 0);
169 }
170
171 } // namespace client
172