mshtml: Add separate task list for tasks dispatching events.
[wine.git] / dlls / mshtml / task.c
blob37667dcc9ccae1a351daa22563869736a504af92
1 /*
2 * Copyright 2006 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
20 #include <stdio.h>
22 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "ole2.h"
29 #include "wine/debug.h"
31 #include "mshtml_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
35 #define WM_PROCESSTASK 0x8008
36 #define TIMER_ID 0x3000
38 typedef struct {
39 HTMLInnerWindow *window;
40 DWORD id;
41 DWORD time;
42 DWORD interval;
43 enum timer_type type;
44 IDispatch *disp;
46 struct list entry;
47 } task_timer_t;
49 HRESULT push_task(task_t *task, task_proc_t proc, task_proc_t destr, LONG magic)
51 thread_data_t *thread_data;
53 thread_data = get_thread_data(TRUE);
54 if(!thread_data) {
55 destr(task);
56 free(task);
57 return E_OUTOFMEMORY;
60 task->target_magic = magic;
61 task->proc = proc;
62 task->destr = destr;
64 list_add_tail(&thread_data->task_list, &task->entry);
66 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
67 return S_OK;
70 static void release_event_task(event_task_t *task)
72 task->destr(task);
73 IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
74 free(task);
77 HRESULT push_event_task(event_task_t *task, HTMLInnerWindow *window, event_task_proc_t proc, event_task_proc_t destr, LONG magic)
79 thread_data_t *thread_data;
81 task->target_magic = magic;
82 task->proc = proc;
83 task->destr = destr;
84 task->window = window;
85 IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
87 thread_data = get_thread_data(TRUE);
88 if(!thread_data) {
89 release_event_task(task);
90 return E_OUTOFMEMORY;
93 list_add_tail(&thread_data->event_task_list, &task->entry);
95 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
96 return S_OK;
99 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
101 list_remove(&timer->entry);
103 IDispatch_Release(timer->disp);
105 free(timer);
108 void remove_target_tasks(LONG target)
110 thread_data_t *thread_data = get_thread_data(FALSE);
111 struct list *liter, *ltmp;
112 task_timer_t *timer;
113 task_t *task;
115 if(!thread_data)
116 return;
118 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
119 timer = LIST_ENTRY(liter, task_timer_t, entry);
120 if(timer->window->task_magic == target)
121 release_task_timer(thread_data->thread_hwnd, timer);
124 if(!list_empty(&thread_data->timer_list)) {
125 DWORD tc = GetTickCount();
127 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
128 SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL);
131 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) {
132 task = LIST_ENTRY(liter, task_t, entry);
133 if(task->target_magic == target) {
134 list_remove(&task->entry);
135 task->destr(task);
136 free(task);
140 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->event_task_list) {
141 event_task_t *task = LIST_ENTRY(liter, event_task_t, entry);
142 if(task->target_magic == target) {
143 list_remove(&task->entry);
144 release_event_task(task);
149 LONG get_task_target_magic(void)
151 static LONG magic = 0x10000000;
152 return InterlockedIncrement(&magic);
155 static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer)
157 task_timer_t *iter;
159 list_remove(&timer->entry);
161 if(list_empty(&thread_data->timer_list)
162 || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) {
164 list_add_head(&thread_data->timer_list, &timer->entry);
165 return TRUE;
168 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
169 if(iter->time > timer->time) {
170 list_add_tail(&iter->entry, &timer->entry);
171 return FALSE;
175 list_add_tail(&thread_data->timer_list, &timer->entry);
176 return FALSE;
179 HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, enum timer_type timer_type, IDispatch *disp, LONG *id)
181 thread_data_t *thread_data;
182 task_timer_t *timer;
183 DWORD tc = GetTickCount();
185 static DWORD id_cnt = 0x20000000;
187 thread_data = get_thread_data(TRUE);
188 if(!thread_data)
189 return E_OUTOFMEMORY;
191 timer = malloc(sizeof(task_timer_t));
192 if(!timer)
193 return E_OUTOFMEMORY;
195 if(msec < 1)
196 msec = 1;
198 timer->id = id_cnt++;
199 timer->window = window;
200 timer->time = tc + msec;
201 timer->interval = timer_type == TIMER_INTERVAL ? msec : 0;
202 timer->type = timer_type;
203 list_init(&timer->entry);
205 IDispatch_AddRef(disp);
206 timer->disp = disp;
208 if(queue_timer(thread_data, timer))
209 SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
211 *id = timer->id;
212 return S_OK;
215 HRESULT clear_task_timer(HTMLInnerWindow *window, DWORD id)
217 thread_data_t *thread_data = get_thread_data(FALSE);
218 task_timer_t *iter;
220 if(!thread_data)
221 return S_OK;
223 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
224 if(iter->id == id && iter->window == window) {
225 if(iter->type != TIMER_ANIMATION_FRAME)
226 release_task_timer(thread_data->thread_hwnd, iter);
227 return S_OK;
231 WARN("timet not found\n");
232 return S_OK;
235 HRESULT clear_animation_timer(HTMLInnerWindow *window, DWORD id)
237 thread_data_t *thread_data = get_thread_data(FALSE);
238 task_timer_t *iter;
240 if(!thread_data)
241 return S_OK;
243 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
244 if(iter->id == id && iter->window == window) {
245 if(iter->type == TIMER_ANIMATION_FRAME)
246 release_task_timer(thread_data->thread_hwnd, iter);
247 return S_OK;
251 WARN("timer not found\n");
252 return S_OK;
255 static const char *debugstr_timer_type(enum timer_type type)
257 switch(type) {
258 case TIMER_TIMEOUT: return "timeout";
259 case TIMER_INTERVAL: return "interval";
260 case TIMER_ANIMATION_FRAME: return "animation-frame";
261 DEFAULT_UNREACHABLE;
265 static void call_timer_disp(IDispatch *disp, enum timer_type timer_type)
267 DISPPARAMS dp = {NULL, NULL, 0, 0};
268 VARIANT timestamp;
269 EXCEPINFO ei;
270 VARIANT res;
271 HRESULT hres;
273 V_VT(&res) = VT_EMPTY;
274 memset(&ei, 0, sizeof(ei));
276 if(timer_type == TIMER_ANIMATION_FRAME) {
277 dp.cArgs = 1;
278 dp.rgvarg = &timestamp;
279 V_VT(&timestamp) = VT_R8;
280 V_R8(&timestamp) = get_time_stamp();
283 TRACE("%p %s >>>\n", disp, debugstr_timer_type(timer_type));
284 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL);
285 if(hres == S_OK)
286 TRACE("%p %s <<<\n", disp, debugstr_timer_type(timer_type));
287 else
288 WARN("%p %s <<< %08lx\n", disp, debugstr_timer_type(timer_type), hres);
290 VariantClear(&res);
293 static LRESULT process_timer(void)
295 thread_data_t *thread_data;
296 enum timer_type timer_type;
297 IDispatch *disp;
298 DWORD tc;
299 task_timer_t *timer=NULL, *last_timer;
301 TRACE("\n");
303 thread_data = get_thread_data(FALSE);
304 assert(thread_data != NULL);
306 if(list_empty(&thread_data->timer_list)) {
307 KillTimer(thread_data->thread_hwnd, TIMER_ID);
308 return 0;
311 last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry);
312 do {
313 tc = GetTickCount();
314 if(timer == last_timer) {
315 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
316 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL);
317 return 0;
320 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
322 if(timer->time > tc) {
323 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
324 return 0;
327 disp = timer->disp;
328 IDispatch_AddRef(disp);
329 timer_type = timer->type;
331 if(timer->interval) {
332 timer->time += timer->interval;
333 queue_timer(thread_data, timer);
334 }else {
335 release_task_timer(thread_data->thread_hwnd, timer);
338 call_timer_disp(disp, timer_type);
340 IDispatch_Release(disp);
341 }while(!list_empty(&thread_data->timer_list));
343 KillTimer(thread_data->thread_hwnd, TIMER_ID);
344 return 0;
347 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
349 thread_data_t *thread_data;
351 switch(msg) {
352 case WM_PROCESSTASK:
353 thread_data = get_thread_data(FALSE);
354 if(!thread_data)
355 return 0;
357 while(1) {
358 struct list *head = list_head(&thread_data->task_list);
360 if(head) {
361 task_t *task = LIST_ENTRY(head, task_t, entry);
362 list_remove(&task->entry);
363 task->proc(task);
364 task->destr(task);
365 free(task);
366 continue;
369 head = list_head(&thread_data->event_task_list);
370 if(head) {
371 event_task_t *task = LIST_ENTRY(head, event_task_t, entry);
372 list_remove(&task->entry);
373 task->proc(task);
374 release_event_task(task);
375 continue;
378 break;
380 return 0;
381 case WM_TIMER:
382 return process_timer();
385 if(msg > WM_USER)
386 FIXME("(%p %d %Ix %Ix)\n", hwnd, msg, wParam, lParam);
388 return DefWindowProcW(hwnd, msg, wParam, lParam);
391 static HWND create_thread_hwnd(void)
393 static ATOM hidden_wnd_class = 0;
395 if(!hidden_wnd_class) {
396 WNDCLASSEXW wndclass = {
397 sizeof(WNDCLASSEXW), 0,
398 hidden_proc,
399 0, 0, hInst, NULL, NULL, NULL, NULL,
400 L"Internet Explorer_Hidden",
401 NULL
404 hidden_wnd_class = RegisterClassExW(&wndclass);
407 return CreateWindowExW(0, L"Internet Explorer_Hidden", NULL, WS_POPUP,
408 0, 0, 0, 0, NULL, NULL, hInst, NULL);
411 HWND get_thread_hwnd(void)
413 thread_data_t *thread_data;
415 thread_data = get_thread_data(TRUE);
416 if(!thread_data)
417 return NULL;
419 if(!thread_data->thread_hwnd)
420 thread_data->thread_hwnd = create_thread_hwnd();
422 return thread_data->thread_hwnd;
425 thread_data_t *get_thread_data(BOOL create)
427 thread_data_t *thread_data;
429 if(mshtml_tls == TLS_OUT_OF_INDEXES) {
430 DWORD tls;
432 if(!create)
433 return NULL;
435 tls = TlsAlloc();
436 if(tls == TLS_OUT_OF_INDEXES)
437 return NULL;
439 tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES);
440 if(tls != mshtml_tls)
441 TlsFree(tls);
444 thread_data = TlsGetValue(mshtml_tls);
445 if(!thread_data && create) {
446 thread_data = calloc(1, sizeof(thread_data_t));
447 if(!thread_data)
448 return NULL;
450 TlsSetValue(mshtml_tls, thread_data);
451 list_init(&thread_data->task_list);
452 list_init(&thread_data->event_task_list);
453 list_init(&thread_data->timer_list);
454 wine_rb_init(&thread_data->session_storage_map, session_storage_map_cmp);
457 return thread_data;
460 ULONGLONG get_time_stamp(void)
462 FILETIME time;
464 /* 1601 to 1970 is 369 years plus 89 leap days */
465 const ULONGLONG time_epoch = (ULONGLONG)(369 * 365 + 89) * 86400 * 1000;
467 GetSystemTimeAsFileTime(&time);
468 return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch;