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