imagehlp: Use the IMAGE_FIRST_SECTION helper macro.
[wine.git] / dlls / mshtml / task.c
blob49f76d101a971014490eb407a5879a99bd2ed19e
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 unlink_event_task(event_task_t *task, thread_data_t *thread_data)
101 if(thread_data->pending_xhr_events_tail == &task->entry)
102 thread_data->pending_xhr_events_tail = task->entry.prev;
103 list_remove(&task->entry);
106 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
108 list_remove(&timer->entry);
110 IDispatch_Release(timer->disp);
112 free(timer);
115 void remove_target_tasks(LONG target)
117 thread_data_t *thread_data = get_thread_data(FALSE);
118 struct list *liter, *ltmp;
119 task_timer_t *timer;
120 task_t *task;
122 if(!thread_data)
123 return;
125 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
126 timer = LIST_ENTRY(liter, task_timer_t, entry);
127 if(timer->window->task_magic == target)
128 release_task_timer(thread_data->thread_hwnd, timer);
131 if(!list_empty(&thread_data->timer_list)) {
132 DWORD tc = GetTickCount();
134 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
135 SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL);
138 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) {
139 task = LIST_ENTRY(liter, task_t, entry);
140 if(task->target_magic == target) {
141 list_remove(&task->entry);
142 task->destr(task);
143 free(task);
147 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->event_task_list) {
148 event_task_t *task = LIST_ENTRY(liter, event_task_t, entry);
149 if(task->target_magic == target) {
150 unlink_event_task(task, thread_data);
151 release_event_task(task);
156 LONG get_task_target_magic(void)
158 static LONG magic = 0x10000000;
159 return InterlockedIncrement(&magic);
162 static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer)
164 task_timer_t *iter;
166 list_remove(&timer->entry);
168 if(list_empty(&thread_data->timer_list)
169 || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) {
171 list_add_head(&thread_data->timer_list, &timer->entry);
172 return TRUE;
175 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
176 if(iter->time > timer->time) {
177 list_add_tail(&iter->entry, &timer->entry);
178 return FALSE;
182 list_add_tail(&thread_data->timer_list, &timer->entry);
183 return FALSE;
186 HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, enum timer_type timer_type, IDispatch *disp, LONG *id)
188 thread_data_t *thread_data;
189 task_timer_t *timer;
190 DWORD tc = GetTickCount();
192 static DWORD id_cnt = 0x20000000;
194 thread_data = get_thread_data(TRUE);
195 if(!thread_data)
196 return E_OUTOFMEMORY;
198 timer = malloc(sizeof(task_timer_t));
199 if(!timer)
200 return E_OUTOFMEMORY;
202 if(msec < 1)
203 msec = 1;
205 timer->id = id_cnt++;
206 timer->window = window;
207 timer->time = tc + msec;
208 timer->interval = timer_type == TIMER_INTERVAL ? msec : 0;
209 timer->type = timer_type;
210 list_init(&timer->entry);
212 IDispatch_AddRef(disp);
213 timer->disp = disp;
215 if(queue_timer(thread_data, timer))
216 SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
218 *id = timer->id;
219 return S_OK;
222 HRESULT clear_task_timer(HTMLInnerWindow *window, DWORD id)
224 thread_data_t *thread_data = get_thread_data(FALSE);
225 task_timer_t *iter;
227 if(!thread_data)
228 return S_OK;
230 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
231 if(iter->id == id && iter->window == window) {
232 if(iter->type != TIMER_ANIMATION_FRAME)
233 release_task_timer(thread_data->thread_hwnd, iter);
234 return S_OK;
238 WARN("timet not found\n");
239 return S_OK;
242 HRESULT clear_animation_timer(HTMLInnerWindow *window, DWORD id)
244 thread_data_t *thread_data = get_thread_data(FALSE);
245 task_timer_t *iter;
247 if(!thread_data)
248 return S_OK;
250 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
251 if(iter->id == id && iter->window == window) {
252 if(iter->type == TIMER_ANIMATION_FRAME)
253 release_task_timer(thread_data->thread_hwnd, iter);
254 return S_OK;
258 WARN("timer not found\n");
259 return S_OK;
262 static const char *debugstr_timer_type(enum timer_type type)
264 switch(type) {
265 case TIMER_TIMEOUT: return "timeout";
266 case TIMER_INTERVAL: return "interval";
267 case TIMER_ANIMATION_FRAME: return "animation-frame";
268 DEFAULT_UNREACHABLE;
272 static void call_timer_disp(IDispatch *disp, enum timer_type timer_type)
274 DISPPARAMS dp = {NULL, NULL, 0, 0};
275 VARIANT timestamp;
276 EXCEPINFO ei;
277 VARIANT res;
278 HRESULT hres;
280 V_VT(&res) = VT_EMPTY;
281 memset(&ei, 0, sizeof(ei));
283 if(timer_type == TIMER_ANIMATION_FRAME) {
284 dp.cArgs = 1;
285 dp.rgvarg = &timestamp;
286 V_VT(&timestamp) = VT_R8;
287 V_R8(&timestamp) = get_time_stamp();
290 TRACE("%p %s >>>\n", disp, debugstr_timer_type(timer_type));
291 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL);
292 if(hres == S_OK)
293 TRACE("%p %s <<<\n", disp, debugstr_timer_type(timer_type));
294 else
295 WARN("%p %s <<< %08lx\n", disp, debugstr_timer_type(timer_type), hres);
297 VariantClear(&res);
300 static LRESULT process_timer(void)
302 thread_data_t *thread_data;
303 enum timer_type timer_type;
304 IDispatch *disp;
305 DWORD tc;
306 task_timer_t *timer=NULL, *last_timer;
308 TRACE("\n");
310 thread_data = get_thread_data(FALSE);
311 assert(thread_data != NULL);
313 if(list_empty(&thread_data->timer_list) || thread_data->blocking_xhr) {
314 KillTimer(thread_data->thread_hwnd, TIMER_ID);
315 return 0;
318 last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry);
319 do {
320 tc = GetTickCount();
321 if(timer == last_timer) {
322 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
323 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL);
324 return 0;
327 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
329 if(timer->time > tc) {
330 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
331 return 0;
334 disp = timer->disp;
335 IDispatch_AddRef(disp);
336 timer_type = timer->type;
338 if(timer->interval) {
339 timer->time += timer->interval;
340 queue_timer(thread_data, timer);
341 }else {
342 release_task_timer(thread_data->thread_hwnd, timer);
345 call_timer_disp(disp, timer_type);
347 IDispatch_Release(disp);
348 }while(!list_empty(&thread_data->timer_list) && !thread_data->blocking_xhr);
350 KillTimer(thread_data->thread_hwnd, TIMER_ID);
351 return 0;
354 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
356 thread_data_t *thread_data;
358 switch(msg) {
359 case WM_PROCESSTASK:
360 thread_data = get_thread_data(FALSE);
361 if(!thread_data)
362 return 0;
364 while(1) {
365 struct list *head = list_head(&thread_data->task_list);
367 if(head) {
368 task_t *task = LIST_ENTRY(head, task_t, entry);
369 list_remove(&task->entry);
370 task->proc(task);
371 task->destr(task);
372 free(task);
373 continue;
376 head = &thread_data->event_task_list;
377 while((head = list_next(&thread_data->event_task_list, head))) {
378 event_task_t *task = LIST_ENTRY(head, event_task_t, entry);
380 if((!task->thread_blocked || !thread_data->blocking_xhr) && !task->window->blocking_depth) {
381 unlink_event_task(task, thread_data);
382 task->proc(task);
383 release_event_task(task);
384 break;
387 if(!head)
388 break;
390 return 0;
391 case WM_TIMER:
392 return process_timer();
395 if(msg > WM_USER)
396 FIXME("(%p %d %Ix %Ix)\n", hwnd, msg, wParam, lParam);
398 return DefWindowProcW(hwnd, msg, wParam, lParam);
401 static HWND create_thread_hwnd(void)
403 static ATOM hidden_wnd_class = 0;
405 if(!hidden_wnd_class) {
406 WNDCLASSEXW wndclass = {
407 sizeof(WNDCLASSEXW), 0,
408 hidden_proc,
409 0, 0, hInst, NULL, NULL, NULL, NULL,
410 L"Internet Explorer_Hidden",
411 NULL
414 hidden_wnd_class = RegisterClassExW(&wndclass);
417 return CreateWindowExW(0, L"Internet Explorer_Hidden", NULL, WS_POPUP,
418 0, 0, 0, 0, NULL, NULL, hInst, NULL);
421 HWND get_thread_hwnd(void)
423 thread_data_t *thread_data;
425 thread_data = get_thread_data(TRUE);
426 if(!thread_data)
427 return NULL;
429 if(!thread_data->thread_hwnd)
430 thread_data->thread_hwnd = create_thread_hwnd();
432 return thread_data->thread_hwnd;
435 thread_data_t *get_thread_data(BOOL create)
437 thread_data_t *thread_data;
439 if(mshtml_tls == TLS_OUT_OF_INDEXES) {
440 DWORD tls;
442 if(!create)
443 return NULL;
445 tls = TlsAlloc();
446 if(tls == TLS_OUT_OF_INDEXES)
447 return NULL;
449 tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES);
450 if(tls != mshtml_tls)
451 TlsFree(tls);
454 thread_data = TlsGetValue(mshtml_tls);
455 if(!thread_data && create) {
456 thread_data = calloc(1, sizeof(thread_data_t));
457 if(!thread_data)
458 return NULL;
460 TlsSetValue(mshtml_tls, thread_data);
461 list_init(&thread_data->task_list);
462 list_init(&thread_data->event_task_list);
463 list_init(&thread_data->timer_list);
464 thread_data->pending_xhr_events_tail = &thread_data->event_task_list;
465 wine_rb_init(&thread_data->session_storage_map, session_storage_map_cmp);
468 return thread_data;
471 ULONGLONG get_time_stamp(void)
473 FILETIME time;
475 /* 1601 to 1970 is 369 years plus 89 leap days */
476 const ULONGLONG time_epoch = (ULONGLONG)(369 * 365 + 89) * 86400 * 1000;
478 GetSystemTimeAsFileTime(&time);
479 return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch;
482 void unblock_tasks_and_timers(thread_data_t *thread_data)
484 if(!list_empty(&thread_data->event_task_list))
485 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
487 if(!thread_data->blocking_xhr && !list_empty(&thread_data->timer_list)) {
488 task_timer_t *timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
489 DWORD tc = GetTickCount();
491 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time > tc ? timer->time - tc : 0, NULL);