rpcrt4: Implement RpcCancelThread for the ncacn_ip_tcp protocol sequence.
[wine] / dlls / mshtml / navigate.c
1 /*
2  * Copyright 2006-2007 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
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "hlguids.h"
32 #include "shlguid.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 #include "mshtml_private.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
40
41 #define CONTENT_LENGTH "Content-Length"
42 #define UTF16_STR "utf-16"
43
44 #define NSINSTREAM(x) ((nsIInputStream*) &(x)->lpInputStreamVtbl)
45
46 #define NSINSTREAM_THIS(iface) DEFINE_THIS(nsProtocolStream, InputStream, iface)
47
48 static nsresult NSAPI nsInputStream_QueryInterface(nsIInputStream *iface, nsIIDRef riid,
49                                                    nsQIResult result)
50 {
51     nsProtocolStream *This = NSINSTREAM_THIS(iface);
52
53     *result = NULL;
54
55     if(IsEqualGUID(&IID_nsISupports, riid)) {
56         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
57         *result  = NSINSTREAM(This);
58     }else if(IsEqualGUID(&IID_nsIInputStream, riid)) {
59         TRACE("(%p)->(IID_nsIInputStream %p)\n", This, result);
60         *result  = NSINSTREAM(This);
61     }
62
63     if(*result) {
64         nsIInputStream_AddRef(NSINSTREAM(This));
65         return NS_OK;
66     }
67
68     WARN("unsupported interface %s\n", debugstr_guid(riid));
69     return NS_NOINTERFACE;
70 }
71
72 static nsrefcnt NSAPI nsInputStream_AddRef(nsIInputStream *iface)
73 {
74     nsProtocolStream *This = NSINSTREAM_THIS(iface);
75     LONG ref = InterlockedIncrement(&This->ref);
76
77     TRACE("(%p) ref=%d\n", This, ref);
78
79     return ref;
80 }
81
82
83 static nsrefcnt NSAPI nsInputStream_Release(nsIInputStream *iface)
84 {
85     nsProtocolStream *This = NSINSTREAM_THIS(iface);
86     LONG ref = InterlockedDecrement(&This->ref);
87
88     TRACE("(%p) ref=%d\n", This, ref);
89
90     if(!ref)
91         mshtml_free(This);
92
93     return ref;
94 }
95
96 static nsresult NSAPI nsInputStream_Close(nsIInputStream *iface)
97 {
98     nsProtocolStream *This = NSINSTREAM_THIS(iface);
99     FIXME("(%p)\n", This);
100     return NS_ERROR_NOT_IMPLEMENTED;
101 }
102
103 static nsresult NSAPI nsInputStream_Available(nsIInputStream *iface, PRUint32 *_retval)
104 {
105     nsProtocolStream *This = NSINSTREAM_THIS(iface);
106     FIXME("(%p)->(%p)\n", This, _retval);
107     return NS_ERROR_NOT_IMPLEMENTED;
108 }
109
110 static nsresult NSAPI nsInputStream_Read(nsIInputStream *iface, char *aBuf, PRUint32 aCount,
111                                          PRUint32 *_retval)
112 {
113     nsProtocolStream *This = NSINSTREAM_THIS(iface);
114
115     TRACE("(%p)->(%p %d %p)\n", This, aBuf, aCount, _retval);
116
117     /* Gecko always calls Read with big enough buffer */
118     if(aCount < This->buf_size)
119         FIXME("aCount < This->buf_size\n");
120
121     *_retval = This->buf_size;
122     if(This->buf_size)
123         memcpy(aBuf, This->buf, This->buf_size);
124     This->buf_size = 0;
125
126     return NS_OK;
127 }
128
129 static nsresult NSAPI nsInputStream_ReadSegments(nsIInputStream *iface,
130         nsresult (WINAPI *aWriter)(nsIInputStream*,void*,const char*,PRUint32,PRUint32,PRUint32*),
131         void *aClousure, PRUint32 aCount, PRUint32 *_retval)
132 {
133     nsProtocolStream *This = NSINSTREAM_THIS(iface);
134     PRUint32 written = 0;
135     nsresult nsres;
136
137     TRACE("(%p)->(%p %p %d %p)\n", This, aWriter, aClousure, aCount, _retval);
138
139     if(!This->buf_size)
140         return S_OK;
141
142     if(This->buf_size > aCount)
143         FIXME("buf_size > aCount\n");
144
145     nsres = aWriter(NSINSTREAM(This), aClousure, This->buf, 0, This->buf_size, &written);
146     if(NS_FAILED(nsres))
147         TRACE("aWritter failed: %08x\n", nsres);
148     else if(written != This->buf_size)
149         FIXME("written %d != buf_size %d\n", written, This->buf_size);
150
151     This->buf_size -= written; 
152
153     *_retval = written;
154     return nsres;
155 }
156
157 static nsresult NSAPI nsInputStream_IsNonBlocking(nsIInputStream *iface, PRBool *_retval)
158 {
159     nsProtocolStream *This = NSINSTREAM_THIS(iface);
160     FIXME("(%p)->(%p)\n", This, _retval);
161     return NS_ERROR_NOT_IMPLEMENTED;
162 }
163
164 #undef NSINSTREAM_THIS
165
166 static const nsIInputStreamVtbl nsInputStreamVtbl = {
167     nsInputStream_QueryInterface,
168     nsInputStream_AddRef,
169     nsInputStream_Release,
170     nsInputStream_Close,
171     nsInputStream_Available,
172     nsInputStream_Read,
173     nsInputStream_ReadSegments,
174     nsInputStream_IsNonBlocking
175 };
176
177 static nsProtocolStream *create_nsprotocol_stream(void)
178 {
179     nsProtocolStream *ret = mshtml_alloc(sizeof(nsProtocolStream));
180
181     ret->lpInputStreamVtbl = &nsInputStreamVtbl;
182     ret->ref = 1;
183     ret->buf_size = 0;
184
185     return ret;
186 }
187
188 static HRESULT read_stream_data(BSCallback *This, IStream *stream)
189 {
190     nsresult nsres;
191     HRESULT hres;
192
193     if(!This->nslistener) {
194         BYTE buf[1024];
195         DWORD read;
196
197         do {
198             read = 0;
199             hres = IStream_Read(stream, buf, sizeof(buf), &read);
200         }while(hres == S_OK && read);
201
202         return S_OK;
203     }
204
205     if(!This->nsstream)
206         This->nsstream = create_nsprotocol_stream();
207
208     do {
209         hres = IStream_Read(stream, This->nsstream->buf, sizeof(This->nsstream->buf),
210                 &This->nsstream->buf_size);
211         if(!This->nsstream->buf_size)
212             break;
213
214         if(!This->readed && This->nsstream->buf_size >= 2 && *(WORD*)This->nsstream->buf == 0xfeff) {
215                 This->nschannel->charset = mshtml_alloc(sizeof(UTF16_STR));
216                 memcpy(This->nschannel->charset, UTF16_STR, sizeof(UTF16_STR));
217         }
218
219         if(!This->readed) {
220             nsres = nsIStreamListener_OnStartRequest(This->nslistener,
221                     (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext);
222             if(NS_FAILED(nsres))
223                 FIXME("OnStartRequest failed: %08x\n", nsres);
224
225             /* events are reset when a new document URI is loaded, so re-initialise them here */
226             if(This->doc && This->doc->bscallback == This && This->doc->nscontainer)
227                 init_nsevents(This->doc->nscontainer);
228         }
229
230         This->readed += This->nsstream->buf_size;
231
232         nsres = nsIStreamListener_OnDataAvailable(This->nslistener,
233                 (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext,
234                 NSINSTREAM(This->nsstream), This->readed-This->nsstream->buf_size,
235                 This->nsstream->buf_size);
236         if(NS_FAILED(nsres))
237             ERR("OnDataAvailable failed: %08x\n", nsres);
238
239         if(This->nsstream->buf_size)
240             FIXME("buffer is not empty!\n");
241     }while(hres == S_OK);
242
243     return S_OK;
244 }
245
246 static void add_nsrequest(BSCallback *This)
247 {
248     if(This->nschannel && This->nschannel->load_group) {
249         nsresult nsres = nsILoadGroup_AddRequest(This->nschannel->load_group,
250                 (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext);
251
252         if(NS_FAILED(nsres))
253             ERR("AddRequest failed:%08x\n", nsres);
254     }
255 }
256
257 static void on_stop_nsrequest(BSCallback *This) {
258     if(This->nslistener)
259         nsIStreamListener_OnStopRequest(This->nslistener, (nsIRequest*)NSCHANNEL(This->nschannel),
260                 This->nscontext, NS_OK);
261 }
262
263 #define STATUSCLB_THIS(iface) DEFINE_THIS(BSCallback, BindStatusCallback, iface)
264
265 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
266         REFIID riid, void **ppv)
267 {
268     BSCallback *This = STATUSCLB_THIS(iface);
269
270     *ppv = NULL;
271     if(IsEqualGUID(&IID_IUnknown, riid)) {
272         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
273         *ppv = STATUSCLB(This);
274     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
275         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv);
276         *ppv = STATUSCLB(This);
277     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
278         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
279         *ppv = SERVPROV(This);
280     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
281         TRACE("(%p)->(IID_IHttpNegotiate %p)\n", This, ppv);
282         *ppv = HTTPNEG(This);
283     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
284         TRACE("(%p)->(IID_IHttpNegotiate2 %p)\n", This, ppv);
285         *ppv = HTTPNEG(This);
286     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
287         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
288         *ppv = BINDINFO(This);
289     }
290
291     if(*ppv) {
292         IBindStatusCallback_AddRef(STATUSCLB(This));
293         return S_OK;
294     }
295
296     TRACE("Unsupported riid = %s\n", debugstr_guid(riid));
297     return E_NOINTERFACE;
298 }
299
300 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
301 {
302     BSCallback *This = STATUSCLB_THIS(iface);
303     LONG ref = InterlockedIncrement(&This->ref);
304
305     TRACE("(%p) ref = %d\n", This, ref);
306
307     return ref;
308 }
309
310 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
311 {
312     BSCallback *This = STATUSCLB_THIS(iface);
313     LONG ref = InterlockedDecrement(&This->ref);
314
315     TRACE("(%p) ref = %d\n", This, ref);
316
317     if(!ref) {
318         if(This->post_data)
319             GlobalFree(This->post_data);
320         if(This->nschannel)
321             nsIChannel_Release(NSCHANNEL(This->nschannel));
322         if(This->nslistener)
323             nsIStreamListener_Release(This->nslistener);
324         if(This->nscontext)
325             nsISupports_Release(This->nscontext);
326         if(This->nsstream)
327             nsIInputStream_Release(NSINSTREAM(This->nsstream));
328         if(This->mon)
329             IMoniker_Release(This->mon);
330         if(This->binding)
331             IBinding_Release(This->binding);
332         list_remove(&This->entry);
333         mshtml_free(This->headers);
334         mshtml_free(This);
335     }
336
337     return ref;
338 }
339
340 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
341         DWORD dwReserved, IBinding *pbind)
342 {
343     BSCallback *This = STATUSCLB_THIS(iface);
344
345     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);
346
347     IBinding_AddRef(pbind);
348     This->binding = pbind;
349
350     if(This->doc)
351         list_add_head(&This->doc->bindings, &This->entry);
352
353     add_nsrequest(This);
354
355     return S_OK;
356 }
357
358 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
359 {
360     BSCallback *This = STATUSCLB_THIS(iface);
361     FIXME("(%p)->(%p)\n", This, pnPriority);
362     return E_NOTIMPL;
363 }
364
365 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
366 {
367     BSCallback *This = STATUSCLB_THIS(iface);
368     FIXME("(%p)->(%d)\n", This, reserved);
369     return E_NOTIMPL;
370 }
371
372 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
373         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
374 {
375     BSCallback *This = STATUSCLB_THIS(iface);
376
377     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
378             debugstr_w(szStatusText));
379
380     switch(ulStatusCode) {
381     case BINDSTATUS_MIMETYPEAVAILABLE: {
382         int len;
383
384         if(!This->nschannel)
385             return S_OK;
386         mshtml_free(This->nschannel->content);
387
388         len = WideCharToMultiByte(CP_ACP, 0, szStatusText, -1, NULL, 0, NULL, NULL);
389         This->nschannel->content = mshtml_alloc(len*sizeof(WCHAR));
390         WideCharToMultiByte(CP_ACP, 0, szStatusText, -1, This->nschannel->content, -1, NULL, NULL);
391     }
392     }
393
394     return S_OK;
395 }
396
397 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
398         HRESULT hresult, LPCWSTR szError)
399 {
400     BSCallback *This = STATUSCLB_THIS(iface);
401
402     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
403
404     if(This->binding) {
405         IBinding_Release(This->binding);
406         This->binding = NULL;
407     }
408
409     on_stop_nsrequest(This);
410
411     if(This->nslistener) {
412         if(This->nschannel->load_group) {
413             nsresult nsres;
414
415             nsres = nsILoadGroup_RemoveRequest(This->nschannel->load_group,
416                     (nsIRequest*)NSCHANNEL(This->nschannel), NULL, NS_OK);
417             if(NS_FAILED(nsres))
418                 ERR("RemoveRequest failed: %08x\n", nsres);
419         }
420     }
421
422     list_remove(&This->entry);
423
424     if(FAILED(hresult))
425         return S_OK;
426
427     if(This->doc && This->doc->bscallback == This && !This->doc->nscontainer) {
428         task_t *task = mshtml_alloc(sizeof(task_t));
429
430         task->doc = This->doc;
431         task->task_id = TASK_PARSECOMPLETE;
432         task->next = NULL;
433
434         /*
435          * This should be done in the worker thread that parses HTML,
436          * but we don't have such thread.
437          */
438         push_task(task);
439     }
440
441     return S_OK;
442 }
443
444 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
445         DWORD *grfBINDF, BINDINFO *pbindinfo)
446 {
447     BSCallback *This = STATUSCLB_THIS(iface);
448     DWORD size;
449
450     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
451
452     *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
453
454     size = pbindinfo->cbSize;
455     memset(pbindinfo, 0, size);
456     pbindinfo->cbSize = size;
457
458     pbindinfo->cbstgmedData = This->post_data_len;
459     pbindinfo->dwCodePage = CP_UTF8;
460     pbindinfo->dwOptions = 0x80000;
461
462     if(This->post_data) {
463         pbindinfo->dwBindVerb = BINDVERB_POST;
464
465         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
466         pbindinfo->stgmedData.u.hGlobal = This->post_data;
467         pbindinfo->stgmedData.pUnkForRelease = (IUnknown*)STATUSCLB(This);
468         IBindStatusCallback_AddRef(STATUSCLB(This));
469     }
470
471     return S_OK;
472 }
473
474 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
475         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
476 {
477     BSCallback *This = STATUSCLB_THIS(iface);
478
479     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
480
481     return read_stream_data(This, pstgmed->u.pstm);
482 }
483
484 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
485         REFIID riid, IUnknown *punk)
486 {
487     BSCallback *This = STATUSCLB_THIS(iface);
488     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);
489     return E_NOTIMPL;
490 }
491
492 #undef STATUSCLB_THIS
493
494 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
495     BindStatusCallback_QueryInterface,
496     BindStatusCallback_AddRef,
497     BindStatusCallback_Release,
498     BindStatusCallback_OnStartBinding,
499     BindStatusCallback_GetPriority,
500     BindStatusCallback_OnLowResource,
501     BindStatusCallback_OnProgress,
502     BindStatusCallback_OnStopBinding,
503     BindStatusCallback_GetBindInfo,
504     BindStatusCallback_OnDataAvailable,
505     BindStatusCallback_OnObjectAvailable
506 };
507
508 #define HTTPNEG_THIS(iface) DEFINE_THIS(BSCallback, HttpNegotiate2, iface)
509
510 static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
511                                                    REFIID riid, void **ppv)
512 {
513     BSCallback *This = HTTPNEG_THIS(iface);
514     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
515 }
516
517 static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface)
518 {
519     BSCallback *This = HTTPNEG_THIS(iface);
520     return IBindStatusCallback_AddRef(STATUSCLB(This));
521 }
522
523 static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface)
524 {
525     BSCallback *This = HTTPNEG_THIS(iface);
526     return IBindStatusCallback_Release(STATUSCLB(This));
527 }
528
529 static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
530         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
531 {
532     BSCallback *This = HTTPNEG_THIS(iface);
533     DWORD size;
534
535     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders),
536           dwReserved, pszAdditionalHeaders);
537
538     if(!This->headers) {
539         *pszAdditionalHeaders = NULL;
540         return S_OK;
541     }
542
543     size = (strlenW(This->headers)+1)*sizeof(WCHAR);
544     *pszAdditionalHeaders = CoTaskMemAlloc(size);
545     memcpy(*pszAdditionalHeaders, This->headers, size);
546
547     return S_OK;
548 }
549
550 static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
551         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
552 {
553     BSCallback *This = HTTPNEG_THIS(iface);
554     FIXME("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
555           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
556     return E_NOTIMPL;
557 }
558
559 static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
560         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
561 {
562     BSCallback *This = HTTPNEG_THIS(iface);
563     FIXME("(%p)->(%p %p %ld)\n", This, pbSecurityId, pcbSecurityId, dwReserved);
564     return E_NOTIMPL;
565 }
566
567 #undef HTTPNEG
568
569 static const IHttpNegotiate2Vtbl HttpNegotiate2Vtbl = {
570     HttpNegotiate_QueryInterface,
571     HttpNegotiate_AddRef,
572     HttpNegotiate_Release,
573     HttpNegotiate_BeginningTransaction,
574     HttpNegotiate_OnResponse,
575     HttpNegotiate_GetRootSecurityId
576 };
577
578 #define BINDINFO_THIS(iface) DEFINE_THIS(BSCallback, InternetBindInfo, iface)
579
580 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
581                                                       REFIID riid, void **ppv)
582 {
583     BSCallback *This = BINDINFO_THIS(iface);
584     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
585 }
586
587 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
588 {
589     BSCallback *This = BINDINFO_THIS(iface);
590     return IBindStatusCallback_AddRef(STATUSCLB(This));
591 }
592
593 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
594 {
595     BSCallback *This = BINDINFO_THIS(iface);
596     return IBindStatusCallback_Release(STATUSCLB(This));
597 }
598
599 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
600                                                    DWORD *grfBINDF, BINDINFO *pbindinfo)
601 {
602     BSCallback *This = BINDINFO_THIS(iface);
603     FIXME("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
604     return E_NOTIMPL;
605 }
606
607 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
608         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
609 {
610     BSCallback *This = BINDINFO_THIS(iface);
611     FIXME("(%p)->(%u %p %u %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
612     return E_NOTIMPL;
613 }
614
615 #undef BINDINFO_THIS
616
617 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
618     InternetBindInfo_QueryInterface,
619     InternetBindInfo_AddRef,
620     InternetBindInfo_Release,
621     InternetBindInfo_GetBindInfo,
622     InternetBindInfo_GetBindString
623 };
624
625 #define SERVPROV_THIS(iface) DEFINE_THIS(BSCallback, ServiceProvider, iface)
626
627 static HRESULT WINAPI BSCServiceProvider_QueryInterface(IServiceProvider *iface,
628                                                         REFIID riid, void **ppv)
629 {
630     BSCallback *This = SERVPROV_THIS(iface);
631     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
632 }
633
634 static ULONG WINAPI BSCServiceProvider_AddRef(IServiceProvider *iface)
635 {
636     BSCallback *This = SERVPROV_THIS(iface);
637     return IBindStatusCallback_AddRef(STATUSCLB(This));
638 }
639
640 static ULONG WINAPI BSCServiceProvider_Release(IServiceProvider *iface)
641 {
642     BSCallback *This = SERVPROV_THIS(iface);
643     return IBindStatusCallback_Release(STATUSCLB(This));
644 }
645
646 static HRESULT WINAPI BSCServiceProvider_QueryService(IServiceProvider *iface,
647         REFGUID guidService, REFIID riid, void **ppv)
648 {
649     BSCallback *This = SERVPROV_THIS(iface);
650     FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
651     return E_NOTIMPL;
652 }
653
654 #undef SERVPROV_THIS
655
656 static const IServiceProviderVtbl ServiceProviderVtbl = {
657     BSCServiceProvider_QueryInterface,
658     BSCServiceProvider_AddRef,
659     BSCServiceProvider_Release,
660     BSCServiceProvider_QueryService
661 };
662
663 BSCallback *create_bscallback(IMoniker *mon)
664 {
665     BSCallback *ret = mshtml_alloc(sizeof(BSCallback));
666
667     ret->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
668     ret->lpServiceProviderVtbl    = &ServiceProviderVtbl;
669     ret->lpHttpNegotiate2Vtbl     = &HttpNegotiate2Vtbl;
670     ret->lpInternetBindInfoVtbl   = &InternetBindInfoVtbl;
671     ret->ref = 1;
672     ret->post_data = NULL;
673     ret->headers = NULL;
674     ret->post_data_len = 0;
675     ret->readed = 0;
676     ret->nschannel = NULL;
677     ret->nslistener = NULL;
678     ret->nscontext = NULL;
679     ret->nsstream = NULL;
680     ret->binding = NULL;
681     ret->doc = NULL;
682
683     list_init(&ret->entry);
684
685     if(mon)
686         IMoniker_AddRef(mon);
687     ret->mon = mon;
688
689     return ret;
690 }
691
692 /* Calls undocumented 84 cmd of CGID_ShellDocView */
693 static void call_docview_84(HTMLDocument *doc)
694 {
695     IOleCommandTarget *olecmd;
696     VARIANT var;
697     HRESULT hres;
698
699     if(!doc->client)
700         return;
701
702     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
703     if(FAILED(hres))
704         return;
705
706     VariantInit(&var);
707     hres = IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 84, 0, NULL, &var);
708     IOleCommandTarget_Release(olecmd);
709     if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
710         FIXME("handle result\n");
711 }
712
713 static void parse_post_data(nsIInputStream *post_data_stream, LPWSTR *headers_ret,
714                             HGLOBAL *post_data_ret, ULONG *post_data_len_ret)
715 {
716     PRUint32 post_data_len = 0, available = 0;
717     HGLOBAL post_data = NULL;
718     LPWSTR headers = NULL;
719     DWORD headers_len = 0, len;
720     const char *ptr, *ptr2, *post_data_end;
721
722     nsIInputStream_Available(post_data_stream, &available);
723     post_data = GlobalAlloc(0, available+1);
724     nsIInputStream_Read(post_data_stream, post_data, available, &post_data_len);
725     
726     TRACE("post_data = %s\n", debugstr_an(post_data, post_data_len));
727
728     ptr = ptr2 = post_data;
729     post_data_end = (const char*)post_data+post_data_len;
730
731     while(ptr < post_data_end && (*ptr != '\r' || ptr[1] != '\n')) {
732         while(ptr < post_data_end && (*ptr != '\r' || ptr[1] != '\n'))
733             ptr++;
734
735         if(!*ptr) {
736             FIXME("*ptr = 0\n");
737             return;
738         }
739
740         ptr += 2;
741
742         if(ptr-ptr2 >= sizeof(CONTENT_LENGTH)
743            && CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
744                              CONTENT_LENGTH, sizeof(CONTENT_LENGTH)-1,
745                              ptr2, sizeof(CONTENT_LENGTH)-1) == CSTR_EQUAL) {
746             ptr2 = ptr;
747             continue;
748         }
749
750         len = MultiByteToWideChar(CP_ACP, 0, ptr2, ptr-ptr2, NULL, 0);
751
752         if(headers)
753             headers = mshtml_realloc(headers,(headers_len+len+1)*sizeof(WCHAR));
754         else
755             headers = mshtml_alloc((len+1)*sizeof(WCHAR));
756
757         len = MultiByteToWideChar(CP_ACP, 0, ptr2, ptr-ptr2, headers+headers_len, -1);
758         headers_len += len;
759
760         ptr2 = ptr;
761     }
762
763     headers[headers_len] = 0;
764     *headers_ret = headers;
765
766     if(ptr >= post_data_end-2) {
767         GlobalFree(post_data);
768         return;
769     }
770
771     ptr += 2;
772
773     if(headers_len) {
774         post_data_len -= ptr-(const char*)post_data;
775         memmove(post_data, ptr, post_data_len);
776         post_data = GlobalReAlloc(post_data, post_data_len+1, 0);
777     }
778
779     *post_data_ret = post_data;
780     *post_data_len_ret = post_data_len;
781 }
782
783 void hlink_frame_navigate(HTMLDocument *doc, IHlinkFrame *hlink_frame,
784                           LPCWSTR uri, nsIInputStream *post_data_stream, DWORD hlnf)
785 {
786     BSCallback *callback;
787     IBindCtx *bindctx;
788     IMoniker *mon;
789     IHlink *hlink;
790     HRESULT hr;
791
792     callback = create_bscallback(NULL);
793
794     if(post_data_stream) {
795         parse_post_data(post_data_stream, &callback->headers, &callback->post_data,
796                         &callback->post_data_len);
797         TRACE("headers = %s post_data = %s\n", debugstr_w(callback->headers),
798               debugstr_an(callback->post_data, callback->post_data_len));
799     }
800
801     hr = CreateAsyncBindCtx(0, STATUSCLB(callback), NULL, &bindctx);
802     if (FAILED(hr)) {
803         IBindStatusCallback_Release(STATUSCLB(callback));
804         return;
805     }
806
807     hr = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER, &IID_IHlink, (LPVOID*)&hlink);
808     if (FAILED(hr)) {
809         IBindCtx_Release(bindctx);
810         IBindStatusCallback_Release(STATUSCLB(callback));
811         return;
812     }
813
814     hr = CreateURLMoniker(NULL, uri, &mon);
815     if (SUCCEEDED(hr)) {
816         IHlink_SetMonikerReference(hlink, 0, mon, NULL);
817
818         if(hlnf & HLNF_OPENINNEWWINDOW) {
819             static const WCHAR wszBlank[] = {'_','b','l','a','n','k',0};
820             IHlink_SetTargetFrameName(hlink, wszBlank); /* FIXME */
821         }
822
823         IHlinkFrame_Navigate(hlink_frame, hlnf, bindctx, STATUSCLB(callback), hlink);
824
825         IMoniker_Release(mon);
826     }
827
828     IBindCtx_Release(bindctx);
829     IBindStatusCallback_Release(STATUSCLB(callback));
830 }
831
832 HRESULT start_binding(HTMLDocument *doc, BSCallback *bscallback)
833 {
834     IStream *str = NULL;
835     IBindCtx *bctx;
836     HRESULT hres;
837
838     bscallback->doc = doc;
839     call_docview_84(bscallback->doc);
840
841     hres = CreateAsyncBindCtx(0, STATUSCLB(bscallback), NULL, &bctx);
842     if(FAILED(hres)) {
843         WARN("CreateAsyncBindCtx failed: %08x\n", hres);
844         on_stop_nsrequest(bscallback);
845         return hres;
846     }
847
848     hres = IMoniker_BindToStorage(bscallback->mon, bctx, NULL, &IID_IStream, (void**)&str);
849     IBindCtx_Release(bctx);
850     if(FAILED(hres)) {
851         WARN("BindToStorage failed: %08x\n", hres);
852         on_stop_nsrequest(bscallback);
853         return hres;
854     }
855
856     if(str)
857         IStream_Release(str);
858
859     IMoniker_Release(bscallback->mon);
860     bscallback->mon = NULL;
861
862     return S_OK;
863 }
864
865 void set_document_bscallback(HTMLDocument *doc, BSCallback *callback)
866 {
867     BSCallback *iter;
868
869     if(doc->bscallback) {
870         if(doc->bscallback->binding)
871             IBinding_Abort(doc->bscallback->binding);
872         doc->bscallback->doc = NULL;
873         IBindStatusCallback_Release(STATUSCLB(doc->bscallback));
874     }
875
876     LIST_FOR_EACH_ENTRY(iter, &doc->bindings, BSCallback, entry) {
877         iter->doc = NULL;
878         list_remove(&iter->entry);
879     }
880
881     doc->bscallback = callback;
882
883     if(callback) {
884         IBindStatusCallback_AddRef(STATUSCLB(callback));
885         callback->doc = doc;
886     }
887 }
888
889 HRESULT load_stream(BSCallback *bscallback, IStream *stream)
890 {
891     HRESULT hres;
892
893     const char text_html[] = "text/html";
894
895     add_nsrequest(bscallback);
896
897     if(bscallback->nschannel) {
898         bscallback->nschannel->content = mshtml_alloc(sizeof(text_html));
899         memcpy(bscallback->nschannel->content, text_html, sizeof(text_html));
900     }
901
902     hres = read_stream_data(bscallback, stream);
903     IBindStatusCallback_OnStopBinding(STATUSCLB(bscallback), hres, ERROR_SUCCESS);
904
905     return hres;
906 }