1 /*
2 * Copyright 2006 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "mshtml_private.h"
20
21 #define WM_PROCESSTASK 0x8008
22 #define TIMER_ID 0x3000
23
24 typedef struct {
25 HTMLInnerWindow *window;
26 DWORD id;
27 DWORD time;
28 DWORD interval;
29 IDispatch *disp;
30
31 struct list entry;
32 } task_timer_t;
33
default_task_destr(task_t * task)34 static void default_task_destr(task_t *task)
35 {
36 heap_free(task);
37 }
38
push_task(task_t * task,task_proc_t proc,task_proc_t destr,LONG magic)39 HRESULT push_task(task_t *task, task_proc_t proc, task_proc_t destr, LONG magic)
40 {
41 thread_data_t *thread_data;
42
43 thread_data = get_thread_data(TRUE);
44 if(!thread_data) {
45 if(destr)
46 destr(task);
47 else
48 heap_free(task);
49 return E_OUTOFMEMORY;
50 }
51
52 task->target_magic = magic;
53 task->proc = proc;
54 task->destr = destr ? destr : default_task_destr;
55
56 list_add_tail(&thread_data->task_list, &task->entry);
57
58 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
59 return S_OK;
60 }
61
pop_task(void)62 static task_t *pop_task(void)
63 {
64 thread_data_t *thread_data;
65 task_t *task;
66
67 thread_data = get_thread_data(FALSE);
68 if(!thread_data)
69 return NULL;
70
71 if(list_empty(&thread_data->task_list))
72 return NULL;
73
74 task = LIST_ENTRY(thread_data->task_list.next, task_t, entry);
75 list_remove(&task->entry);
76 return task;
77 }
78
release_task_timer(HWND thread_hwnd,task_timer_t * timer)79 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
80 {
81 list_remove(&timer->entry);
82
83 IDispatch_Release(timer->disp);
84
85 heap_free(timer);
86 }
87
flush_pending_tasks(LONG target)88 void flush_pending_tasks(LONG target)
89 {
90 thread_data_t *thread_data = get_thread_data(FALSE);
91 struct list *liter, *ltmp;
92 task_t *task;
93
94 if(!thread_data)
95 return;
96
97 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) {
98 task = LIST_ENTRY(liter, task_t, entry);
99 if(task->target_magic == target) {
100 list_remove(&task->entry);
101 task->proc(task);
102 task->destr(task);
103 }
104 }
105 }
106
remove_target_tasks(LONG target)107 void remove_target_tasks(LONG target)
108 {
109 thread_data_t *thread_data = get_thread_data(FALSE);
110 struct list *liter, *ltmp;
111 task_timer_t *timer;
112 task_t *task;
113
114 if(!thread_data)
115 return;
116
117 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
118 timer = LIST_ENTRY(liter, task_timer_t, entry);
119 if(timer->window->task_magic == target)
120 release_task_timer(thread_data->thread_hwnd, timer);
121 }
122
123 if(!list_empty(&thread_data->timer_list)) {
124 DWORD tc = GetTickCount();
125
126 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
127 SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL);
128 }
129
130 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) {
131 task = LIST_ENTRY(liter, task_t, entry);
132 if(task->target_magic == target) {
133 list_remove(&task->entry);
134 task->destr(task);
135 }
136 }
137 }
138
get_task_target_magic(void)139 LONG get_task_target_magic(void)
140 {
141 static LONG magic = 0x10000000;
142 return InterlockedIncrement(&magic);
143 }
144
queue_timer(thread_data_t * thread_data,task_timer_t * timer)145 static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer)
146 {
147 task_timer_t *iter;
148
149 list_remove(&timer->entry);
150
151 if(list_empty(&thread_data->timer_list)
152 || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) {
153
154 list_add_head(&thread_data->timer_list, &timer->entry);
155 return TRUE;
156 }
157
158 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
159 if(iter->time > timer->time) {
160 list_add_tail(&iter->entry, &timer->entry);
161 return FALSE;
162 }
163 }
164
165 list_add_tail(&thread_data->timer_list, &timer->entry);
166 return FALSE;
167 }
168
set_task_timer(HTMLInnerWindow * window,DWORD msec,BOOL interval,IDispatch * disp,LONG * id)169 HRESULT set_task_timer(HTMLInnerWindow *window, DWORD msec, BOOL interval, IDispatch *disp, LONG *id)
170 {
171 thread_data_t *thread_data;
172 task_timer_t *timer;
173 DWORD tc = GetTickCount();
174
175 static DWORD id_cnt = 0x20000000;
176
177 thread_data = get_thread_data(TRUE);
178 if(!thread_data)
179 return E_OUTOFMEMORY;
180
181 timer = heap_alloc(sizeof(task_timer_t));
182 if(!timer)
183 return E_OUTOFMEMORY;
184
185 timer->id = id_cnt++;
186 timer->window = window;
187 timer->time = tc + msec;
188 timer->interval = interval ? msec : 0;
189 list_init(&timer->entry);
190
191 IDispatch_AddRef(disp);
192 timer->disp = disp;
193
194 if(queue_timer(thread_data, timer))
195 SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
196
197 *id = timer->id;
198 return S_OK;
199 }
200
clear_task_timer(HTMLInnerWindow * window,BOOL interval,DWORD id)201 HRESULT clear_task_timer(HTMLInnerWindow *window, BOOL interval, DWORD id)
202 {
203 thread_data_t *thread_data = get_thread_data(FALSE);
204 task_timer_t *iter;
205
206 if(!thread_data)
207 return S_OK;
208
209 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
210 if(iter->id == id && iter->window == window && !iter->interval == !interval) {
211 release_task_timer(thread_data->thread_hwnd, iter);
212 return S_OK;
213 }
214 }
215
216 WARN("timet not found\n");
217 return S_OK;
218 }
219
call_timer_disp(IDispatch * disp)220 static void call_timer_disp(IDispatch *disp)
221 {
222 DISPPARAMS dp = {NULL, NULL, 0, 0};
223 EXCEPINFO ei;
224 VARIANT res;
225 HRESULT hres;
226
227 V_VT(&res) = VT_EMPTY;
228 memset(&ei, 0, sizeof(ei));
229
230 TRACE(">>>\n");
231 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL);
232 if(hres == S_OK)
233 TRACE("<<<\n");
234 else
235 WARN("<<< %08x\n", hres);
236
237 VariantClear(&res);
238 }
239
process_timer(void)240 static LRESULT process_timer(void)
241 {
242 thread_data_t *thread_data;
243 IDispatch *disp;
244 DWORD tc;
245 task_timer_t *timer=NULL, *last_timer;
246
247 TRACE("\n");
248
249 thread_data = get_thread_data(FALSE);
250 assert(thread_data != NULL);
251
252 if(list_empty(&thread_data->timer_list)) {
253 KillTimer(thread_data->thread_hwnd, TIMER_ID);
254 return 0;
255 }
256
257 last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry);
258 do {
259 tc = GetTickCount();
260 if(timer == last_timer) {
261 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
262 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL);
263 return 0;
264 }
265
266 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
267
268 if(timer->time > tc) {
269 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
270 return 0;
271 }
272
273 disp = timer->disp;
274 IDispatch_AddRef(disp);
275
276 if(timer->interval) {
277 timer->time += timer->interval;
278 queue_timer(thread_data, timer);
279 }else {
280 release_task_timer(thread_data->thread_hwnd, timer);
281 }
282
283 call_timer_disp(disp);
284
285 IDispatch_Release(disp);
286 }while(!list_empty(&thread_data->timer_list));
287
288 KillTimer(thread_data->thread_hwnd, TIMER_ID);
289 return 0;
290 }
291
hidden_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)292 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
293 {
294 switch(msg) {
295 case WM_PROCESSTASK:
296 while(1) {
297 task_t *task = pop_task();
298 if(!task)
299 break;
300
301 task->proc(task);
302 task->destr(task);
303 }
304
305 return 0;
306 case WM_TIMER:
307 return process_timer();
308 }
309
310 if(msg > WM_USER)
311 FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam);
312
313 return DefWindowProcW(hwnd, msg, wParam, lParam);
314 }
315
create_thread_hwnd(void)316 static HWND create_thread_hwnd(void)
317 {
318 static ATOM hidden_wnd_class = 0;
319 static const WCHAR wszInternetExplorer_Hidden[] = {'I','n','t','e','r','n','e','t',
320 ' ','E','x','p','l','o','r','e','r','_','H','i','d','d','e','n',0};
321
322 if(!hidden_wnd_class) {
323 WNDCLASSEXW wndclass = {
324 sizeof(WNDCLASSEXW), 0,
325 hidden_proc,
326 0, 0, hInst, NULL, NULL, NULL, NULL,
327 wszInternetExplorer_Hidden,
328 NULL
329 };
330
331 hidden_wnd_class = RegisterClassExW(&wndclass);
332 }
333
334 return CreateWindowExW(0, wszInternetExplorer_Hidden, NULL, WS_POPUP,
335 0, 0, 0, 0, NULL, NULL, hInst, NULL);
336 }
337
get_thread_hwnd(void)338 HWND get_thread_hwnd(void)
339 {
340 thread_data_t *thread_data;
341
342 thread_data = get_thread_data(TRUE);
343 if(!thread_data)
344 return NULL;
345
346 if(!thread_data->thread_hwnd)
347 thread_data->thread_hwnd = create_thread_hwnd();
348
349 return thread_data->thread_hwnd;
350 }
351
get_thread_data(BOOL create)352 thread_data_t *get_thread_data(BOOL create)
353 {
354 thread_data_t *thread_data;
355
356 if(mshtml_tls == TLS_OUT_OF_INDEXES) {
357 DWORD tls;
358
359 if(!create)
360 return NULL;
361
362 tls = TlsAlloc();
363 if(tls == TLS_OUT_OF_INDEXES)
364 return NULL;
365
366 tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES);
367 if(tls != mshtml_tls)
368 TlsFree(tls);
369 }
370
371 thread_data = TlsGetValue(mshtml_tls);
372 if(!thread_data && create) {
373 thread_data = heap_alloc_zero(sizeof(thread_data_t));
374 if(!thread_data)
375 return NULL;
376
377 TlsSetValue(mshtml_tls, thread_data);
378 list_init(&thread_data->task_list);
379 list_init(&thread_data->timer_list);
380 }
381
382 return thread_data;
383 }
384