iphlpapi/tests: Fix crash on Vista.
[wine] / dlls / 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 "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "ole2.h"
30 #include "mshtmcid.h"
31 #include "shlguid.h"
32
33 #include "wine/debug.h"
34
35 #include "mshtml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
38
39 #define WM_PROCESSTASK 0x8008
40 #define TIMER_ID 0x3000
41
42 typedef struct {
43     HTMLDocument *doc;
44     DWORD id;
45     DWORD time;
46     IDispatch *disp;
47
48     struct list entry;
49 } task_timer_t;
50
51 void push_task(task_t *task)
52 {
53     thread_data_t *thread_data = get_thread_data(TRUE);
54
55     if(thread_data->task_queue_tail)
56         thread_data->task_queue_tail->next = task;
57     else
58         thread_data->task_queue_head = task;
59
60     thread_data->task_queue_tail = task;
61
62     PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
63 }
64
65 static task_t *pop_task(void)
66 {
67     thread_data_t *thread_data = get_thread_data(TRUE);
68     task_t *task = thread_data->task_queue_head;
69
70     if(!task)
71         return NULL;
72
73     thread_data->task_queue_head = task->next;
74     if(!thread_data->task_queue_head)
75         thread_data->task_queue_tail = NULL;
76
77     return task;
78 }
79
80 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
81 {
82     list_remove(&timer->entry);
83
84     KillTimer(thread_hwnd, timer->id);
85     IDispatch_Release(timer->disp);
86
87     heap_free(timer);
88 }
89
90 void remove_doc_tasks(const HTMLDocument *doc)
91 {
92     thread_data_t *thread_data = get_thread_data(FALSE);
93     struct list *liter, *ltmp;
94     task_timer_t *timer;
95     task_t *iter, *tmp;
96
97     LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
98         timer = LIST_ENTRY(liter, task_timer_t, entry);
99         if(timer->doc == doc)
100             release_task_timer(thread_data->thread_hwnd, timer);
101     }
102
103     if(!list_empty(&thread_data->timer_list)) {
104         timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
105         SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time - GetTickCount(), NULL);
106     }
107
108     if(!thread_data)
109         return;
110
111     while(thread_data->task_queue_head
112           && thread_data->task_queue_head->doc == doc)
113         pop_task();
114
115     for(iter = thread_data->task_queue_head; iter; iter = iter->next) {
116         while(iter->next && iter->next->doc == doc) {
117             tmp = iter->next;
118             iter->next = tmp->next;
119             heap_free(tmp);
120         }
121
122         if(!iter->next)
123             thread_data->task_queue_tail = iter;
124     }
125 }
126
127 DWORD set_task_timer(HTMLDocument *doc, DWORD msec, IDispatch *disp)
128 {
129     thread_data_t *thread_data = get_thread_data(TRUE);
130     task_timer_t *timer;
131     DWORD tc = GetTickCount();
132
133     static DWORD id_cnt = 0x20000000;
134
135     timer = heap_alloc(sizeof(task_timer_t));
136     timer->id = id_cnt++;
137     timer->doc = doc;
138     timer->time = tc + msec;
139
140     IDispatch_AddRef(disp);
141     timer->disp = disp;
142
143     if(list_empty(&thread_data->timer_list)
144        || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) {
145
146         list_add_head(&thread_data->timer_list, &timer->entry);
147         SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
148     }else {
149         task_timer_t *iter;
150
151         LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) {
152             if(iter->time > timer->time) {
153                 list_add_tail(&iter->entry, &timer->entry);
154                 return timer->id;
155             }
156         }
157
158         list_add_tail(&thread_data->timer_list, &timer->entry);
159     }
160
161     return timer->id;
162 }
163
164 static void set_downloading(HTMLDocument *doc)
165 {
166     IOleCommandTarget *olecmd;
167     HRESULT hres;
168
169     TRACE("(%p)\n", doc);
170
171     if(doc->frame)
172         IOleInPlaceFrame_SetStatusText(doc->frame, NULL /* FIXME */);
173
174     if(!doc->client)
175         return;
176
177     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
178     if(SUCCEEDED(hres)) {
179         VARIANT var;
180
181         V_VT(&var) = VT_I4;
182         V_I4(&var) = 1;
183
184         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETDOWNLOADSTATE, OLECMDEXECOPT_DONTPROMPTUSER,
185                                &var, NULL);
186         IOleCommandTarget_Release(olecmd);
187     }
188
189     if(doc->hostui) {
190         IDropTarget *drop_target = NULL;
191
192         hres = IDocHostUIHandler_GetDropTarget(doc->hostui, NULL /* FIXME */, &drop_target);
193         if(drop_target) {
194             FIXME("Use IDropTarget\n");
195             IDropTarget_Release(drop_target);
196         }
197     }
198 }
199
200 /* Calls undocumented 69 cmd of CGID_Explorer */
201 static void call_explorer_69(HTMLDocument *doc)
202 {
203     IOleCommandTarget *olecmd;
204     VARIANT var;
205     HRESULT hres;
206
207     if(!doc->client)
208         return;
209
210     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
211     if(FAILED(hres))
212         return;
213
214     VariantInit(&var);
215     hres = IOleCommandTarget_Exec(olecmd, &CGID_Explorer, 69, 0, NULL, &var);
216     IOleCommandTarget_Release(olecmd);
217     if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
218         FIXME("handle result\n");
219 }
220
221 static void set_parsecomplete(HTMLDocument *doc)
222 {
223     IOleCommandTarget *olecmd = NULL;
224
225     TRACE("(%p)\n", doc);
226
227     if(doc->usermode == EDITMODE)
228         init_editor(doc);
229
230     call_explorer_69(doc);
231     call_property_onchanged(&doc->cp_propnotif, 1005);
232     call_explorer_69(doc);
233
234     doc->readystate = READYSTATE_INTERACTIVE;
235     call_property_onchanged(&doc->cp_propnotif, DISPID_READYSTATE);
236
237     if(doc->client)
238         IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
239
240     if(olecmd) {
241         VARIANT state, progress;
242
243         V_VT(&progress) = VT_I4;
244         V_I4(&progress) = 0;
245         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS, OLECMDEXECOPT_DONTPROMPTUSER,
246                                &progress, NULL);
247
248         V_VT(&state) = VT_I4;
249         V_I4(&state) = 0;
250         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETDOWNLOADSTATE, OLECMDEXECOPT_DONTPROMPTUSER,
251                                &state, NULL);
252
253         IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 103, 0, NULL, NULL);
254         IOleCommandTarget_Exec(olecmd, &CGID_MSHTML, IDM_PARSECOMPLETE, 0, NULL, NULL);
255         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_HTTPEQUIV_DONE, 0, NULL, NULL);
256
257         IOleCommandTarget_Release(olecmd);
258     }
259
260     doc->readystate = READYSTATE_COMPLETE;
261     call_property_onchanged(&doc->cp_propnotif, DISPID_READYSTATE);
262
263     if(doc->frame) {
264         static const WCHAR wszDone[] = {'D','o','n','e',0};
265         IOleInPlaceFrame_SetStatusText(doc->frame, wszDone);
266     }
267
268     update_title(doc);
269 }
270
271 static void set_progress(HTMLDocument *doc)
272 {
273     IOleCommandTarget *olecmd = NULL;
274     HRESULT hres;
275
276     TRACE("(%p)\n", doc);
277
278     if(doc->client)
279         IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
280
281     if(olecmd) {
282         VARIANT progress_max, progress;
283
284         V_VT(&progress_max) = VT_I4;
285         V_I4(&progress_max) = 0; /* FIXME */
286         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSMAX, OLECMDEXECOPT_DONTPROMPTUSER,
287                                &progress_max, NULL);
288
289         V_VT(&progress) = VT_I4;
290         V_I4(&progress) = 0; /* FIXME */
291         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS, OLECMDEXECOPT_DONTPROMPTUSER,
292                                &progress, NULL);
293     }
294
295     if(doc->usermode == EDITMODE && doc->hostui) {
296         DOCHOSTUIINFO hostinfo;
297
298         memset(&hostinfo, 0, sizeof(DOCHOSTUIINFO));
299         hostinfo.cbSize = sizeof(DOCHOSTUIINFO);
300         hres = IDocHostUIHandler_GetHostInfo(doc->hostui, &hostinfo);
301         if(SUCCEEDED(hres))
302             /* FIXME: use hostinfo */
303             TRACE("hostinfo = {%u %08x %08x %s %s}\n",
304                     hostinfo.cbSize, hostinfo.dwFlags, hostinfo.dwDoubleClick,
305                     debugstr_w(hostinfo.pchHostCss), debugstr_w(hostinfo.pchHostNS));
306     }
307 }
308
309 static void task_start_binding(HTMLDocument *doc, BSCallback *bscallback)
310 {
311     if(doc)
312         start_binding(doc, bscallback, NULL);
313     IUnknown_Release((IUnknown*)bscallback);
314 }
315
316 static void process_task(task_t *task)
317 {
318     switch(task->task_id) {
319     case TASK_SETDOWNLOADSTATE:
320         set_downloading(task->doc);
321         break;
322     case TASK_PARSECOMPLETE:
323         set_parsecomplete(task->doc);
324         break;
325     case TASK_SETPROGRESS:
326         set_progress(task->doc);
327         break;
328     case TASK_START_BINDING:
329         task_start_binding(task->doc, (BSCallback*)task->bscallback);
330         break;
331     default:
332         ERR("Wrong task_id %d\n", task->task_id);
333     }
334 }
335
336 static LRESULT process_timer(void)
337 {
338     thread_data_t *thread_data = get_thread_data(TRUE);
339     DWORD tc = GetTickCount();
340     task_timer_t *timer;
341
342     while(!list_empty(&thread_data->timer_list)) {
343         timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
344         if(timer->time > tc) {
345             SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
346             return 0;
347         }
348
349         list_remove(&timer->entry);
350         call_disp_func(timer->doc, timer->disp);
351         release_task_timer(thread_data->thread_hwnd, timer);
352     }
353
354     KillTimer(thread_data->thread_hwnd, TIMER_ID);
355     return 0;
356 }
357
358 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
359 {
360     switch(msg) {
361     case WM_PROCESSTASK:
362         while(1) {
363             task_t *task = pop_task();
364             if(!task)
365                 break;
366
367             process_task(task);
368             heap_free(task);
369         }
370
371         return 0;
372     case WM_TIMER:
373         return process_timer();
374     }
375
376     if(msg > WM_USER)
377         FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam);
378
379     return DefWindowProcW(hwnd, msg, wParam, lParam);
380 }
381
382 static HWND create_thread_hwnd(void)
383 {
384     static ATOM hidden_wnd_class = 0;
385     static const WCHAR wszInternetExplorer_Hidden[] = {'I','n','t','e','r','n','e','t',
386             ' ','E','x','p','l','o','r','e','r','_','H','i','d','d','e','n',0};
387
388     if(!hidden_wnd_class) {
389         WNDCLASSEXW wndclass = {
390             sizeof(WNDCLASSEXW), 0,
391             hidden_proc,
392             0, 0, hInst, NULL, NULL, NULL, NULL,
393             wszInternetExplorer_Hidden,
394             NULL
395         };
396
397         hidden_wnd_class = RegisterClassExW(&wndclass);
398     }
399
400     return CreateWindowExW(0, wszInternetExplorer_Hidden, NULL, WS_POPUP,
401                            0, 0, 0, 0, NULL, NULL, hInst, NULL);
402 }
403
404 HWND get_thread_hwnd(void)
405 {
406     thread_data_t *thread_data = get_thread_data(TRUE);
407
408     if(!thread_data->thread_hwnd)
409         thread_data->thread_hwnd = create_thread_hwnd();
410
411     return thread_data->thread_hwnd;
412 }
413
414 thread_data_t *get_thread_data(BOOL create)
415 {
416     thread_data_t *thread_data;
417
418     if(!mshtml_tls) {
419         if(create)
420             mshtml_tls = TlsAlloc();
421         else
422             return NULL;
423     }
424
425     thread_data = TlsGetValue(mshtml_tls);
426     if(!thread_data && create) {
427         thread_data = heap_alloc_zero(sizeof(thread_data_t));
428         TlsSetValue(mshtml_tls, thread_data);
429         list_init(&thread_data->timer_list);
430     }
431
432     return thread_data;
433 }