xref: /reactos/dll/win32/mshtml/task.c (revision c2c66aff)
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