[MSHTML]
[reactos.git] / reactos / dll / win32 / mshtml / task.c
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
34 static void default_task_destr(task_t *task)
35 {
36 heap_free(task);
37 }
38
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 task->next = NULL;
56
57 if(thread_data->task_queue_tail)
58 thread_data->task_queue_tail->next = task;
59 else
60 thread_data->task_queue_head = task;
61
62 thread_data->task_queue_tail = task;
63
64 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
65 return S_OK;
66 }
67
68 static task_t *pop_task(void)
69 {
70 thread_data_t *thread_data;
71 task_t *task;
72
73 thread_data = get_thread_data(FALSE);
74 if(!thread_data)
75 return NULL;
76
77 task = thread_data->task_queue_head;
78 if(!task)
79 return NULL;
80
81 thread_data->task_queue_head = task->next;
82 if(!thread_data->task_queue_head)
83 thread_data->task_queue_tail = NULL;
84
85 return task;
86 }
87
88 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
89 {
90 list_remove(&timer->entry);
91
92 IDispatch_Release(timer->disp);
93
94 heap_free(timer);
95 }
96
97 void remove_target_tasks(LONG target)
98 {
99 thread_data_t *thread_data = get_thread_data(FALSE);
100 struct list *liter, *ltmp;
101 task_timer_t *timer;
102 task_t *iter, *tmp;
103
104 if(!thread_data)
105 return;
106
107 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
108 timer = LIST_ENTRY(liter, task_timer_t, entry);
109 if(timer->window->task_magic == target)
110 release_task_timer(thread_data->thread_hwnd, timer);
111 }
112
113 if(!list_empty(&thread_data->timer_list)) {
114 DWORD tc = GetTickCount();
115
116 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
117 SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL);
118 }
119
120 while(thread_data->task_queue_head && thread_data->task_queue_head->target_magic == target) {
121 iter = pop_task();
122 iter->destr(iter);
123 }
124
125 for(iter = thread_data->task_queue_head; iter; iter = iter->next) {
126 while(iter->next && iter->next->target_magic == target) {
127 tmp = iter->next;
128 iter->next = tmp->next;
129 tmp->destr(tmp);
130 }
131
132 if(!iter->next)
133 thread_data->task_queue_tail = iter;
134 }
135 }
136
137 LONG get_task_target_magic(void)
138 {
139 static LONG magic = 0x10000000;
140 return InterlockedIncrement(&magic);
141 }
142
143 static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer)
144 {
145 task_timer_t *iter;
146
147 list_remove(&timer->entry);
148
149 if(list_empty(&thread_data->timer_list)
150 || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) {
151
152 list_add_head(&thread_data->timer_list, &timer->entry);
153 return TRUE;
154 }
155
156 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
157 if(iter->time > timer->time) {
158 list_add_tail(&iter->entry, &timer->entry);
159 return FALSE;
160 }
161 }
162
163 list_add_tail(&thread_data->timer_list, &timer->entry);
164 return FALSE;
165 }
166
167 HRESULT set_task_timer(HTMLInnerWindow *window, DWORD msec, BOOL interval, IDispatch *disp, LONG *id)
168 {
169 thread_data_t *thread_data;
170 task_timer_t *timer;
171 DWORD tc = GetTickCount();
172
173 static DWORD id_cnt = 0x20000000;
174
175 thread_data = get_thread_data(TRUE);
176 if(!thread_data)
177 return E_OUTOFMEMORY;
178
179 timer = heap_alloc(sizeof(task_timer_t));
180 if(!timer)
181 return E_OUTOFMEMORY;
182
183 timer->id = id_cnt++;
184 timer->window = window;
185 timer->time = tc + msec;
186 timer->interval = interval ? msec : 0;
187 list_init(&timer->entry);
188
189 IDispatch_AddRef(disp);
190 timer->disp = disp;
191
192 if(queue_timer(thread_data, timer))
193 SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
194
195 *id = timer->id;
196 return S_OK;
197 }
198
199 HRESULT clear_task_timer(HTMLInnerWindow *window, BOOL interval, DWORD id)
200 {
201 thread_data_t *thread_data = get_thread_data(FALSE);
202 task_timer_t *iter;
203
204 if(!thread_data)
205 return S_OK;
206
207 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
208 if(iter->id == id && iter->window == window && !iter->interval == !interval) {
209 release_task_timer(thread_data->thread_hwnd, iter);
210 return S_OK;
211 }
212 }
213
214 WARN("timet not found\n");
215 return S_OK;
216 }
217
218 static void call_timer_disp(IDispatch *disp)
219 {
220 DISPPARAMS dp = {NULL, NULL, 0, 0};
221 EXCEPINFO ei;
222 VARIANT res;
223 HRESULT hres;
224
225 V_VT(&res) = VT_EMPTY;
226 memset(&ei, 0, sizeof(ei));
227
228 TRACE(">>>\n");
229 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL);
230 if(hres == S_OK)
231 TRACE("<<<\n");
232 else
233 WARN("<<< %08x\n", hres);
234
235 VariantClear(&res);
236 }
237
238 static LRESULT process_timer(void)
239 {
240 thread_data_t *thread_data;
241 IDispatch *disp;
242 DWORD tc;
243 task_timer_t *timer=NULL, *last_timer;
244
245 TRACE("\n");
246
247 thread_data = get_thread_data(FALSE);
248 assert(thread_data != NULL);
249
250 if(list_empty(&thread_data->timer_list)) {
251 KillTimer(thread_data->thread_hwnd, TIMER_ID);
252 return 0;
253 }
254
255 last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry);
256 do {
257 tc = GetTickCount();
258 if(timer == last_timer) {
259 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
260 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL);
261 return 0;
262 }
263
264 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
265
266 if(timer->time > tc) {
267 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
268 return 0;
269 }
270
271 disp = timer->disp;
272 IDispatch_AddRef(disp);
273
274 if(timer->interval) {
275 timer->time += timer->interval;
276 queue_timer(thread_data, timer);
277 }else {
278 release_task_timer(thread_data->thread_hwnd, timer);
279 }
280
281 call_timer_disp(disp);
282
283 IDispatch_Release(disp);
284 }while(!list_empty(&thread_data->timer_list));
285
286 KillTimer(thread_data->thread_hwnd, TIMER_ID);
287 return 0;
288 }
289
290 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
291 {
292 switch(msg) {
293 case WM_PROCESSTASK:
294 while(1) {
295 task_t *task = pop_task();
296 if(!task)
297 break;
298
299 task->proc(task);
300 task->destr(task);
301 }
302
303 return 0;
304 case WM_TIMER:
305 return process_timer();
306 }
307
308 if(msg > WM_USER)
309 FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam);
310
311 return DefWindowProcW(hwnd, msg, wParam, lParam);
312 }
313
314 static HWND create_thread_hwnd(void)
315 {
316 static ATOM hidden_wnd_class = 0;
317 static const WCHAR wszInternetExplorer_Hidden[] = {'I','n','t','e','r','n','e','t',
318 ' ','E','x','p','l','o','r','e','r','_','H','i','d','d','e','n',0};
319
320 if(!hidden_wnd_class) {
321 WNDCLASSEXW wndclass = {
322 sizeof(WNDCLASSEXW), 0,
323 hidden_proc,
324 0, 0, hInst, NULL, NULL, NULL, NULL,
325 wszInternetExplorer_Hidden,
326 NULL
327 };
328
329 hidden_wnd_class = RegisterClassExW(&wndclass);
330 }
331
332 return CreateWindowExW(0, wszInternetExplorer_Hidden, NULL, WS_POPUP,
333 0, 0, 0, 0, NULL, NULL, hInst, NULL);
334 }
335
336 HWND get_thread_hwnd(void)
337 {
338 thread_data_t *thread_data;
339
340 thread_data = get_thread_data(TRUE);
341 if(!thread_data)
342 return NULL;
343
344 if(!thread_data->thread_hwnd)
345 thread_data->thread_hwnd = create_thread_hwnd();
346
347 return thread_data->thread_hwnd;
348 }
349
350 thread_data_t *get_thread_data(BOOL create)
351 {
352 thread_data_t *thread_data;
353
354 if(mshtml_tls == TLS_OUT_OF_INDEXES) {
355 DWORD tls;
356
357 if(!create)
358 return NULL;
359
360 tls = TlsAlloc();
361 if(tls == TLS_OUT_OF_INDEXES)
362 return NULL;
363
364 tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES);
365 if(tls != mshtml_tls)
366 TlsFree(tls);
367 }
368
369 thread_data = TlsGetValue(mshtml_tls);
370 if(!thread_data && create) {
371 thread_data = heap_alloc_zero(sizeof(thread_data_t));
372 if(!thread_data)
373 return NULL;
374
375 TlsSetValue(mshtml_tls, thread_data);
376 list_init(&thread_data->timer_list);
377 }
378
379 return thread_data;
380 }