mshtml: Added createDocumentFragment tests.
[wine] / dlls / mshtml / navigate.c
1 /*
2  * Copyright 2006-2010 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 "winreg.h"
31 #include "ole2.h"
32 #include "hlguids.h"
33 #include "shlguid.h"
34 #include "wininet.h"
35 #include "shlwapi.h"
36
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 #include "mshtml_private.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
43
44 #define CONTENT_LENGTH "Content-Length"
45 #define UTF16_STR "utf-16"
46
47 typedef struct {
48     const nsIInputStreamVtbl *lpInputStreamVtbl;
49
50     LONG ref;
51
52     char buf[1024];
53     DWORD buf_size;
54 } nsProtocolStream;
55
56 #define NSINSTREAM(x) ((nsIInputStream*) &(x)->lpInputStreamVtbl)
57
58 typedef struct {
59     void (*destroy)(BSCallback*);
60     HRESULT (*init_bindinfo)(BSCallback*);
61     HRESULT (*start_binding)(BSCallback*);
62     HRESULT (*stop_binding)(BSCallback*,HRESULT);
63     HRESULT (*read_data)(BSCallback*,IStream*);
64     HRESULT (*on_progress)(BSCallback*,ULONG,LPCWSTR);
65     HRESULT (*on_response)(BSCallback*,DWORD,LPCWSTR);
66     HRESULT (*beginning_transaction)(BSCallback*,WCHAR**);
67 } BSCallbackVtbl;
68
69 struct BSCallback {
70     const IBindStatusCallbackVtbl *lpBindStatusCallbackVtbl;
71     const IServiceProviderVtbl    *lpServiceProviderVtbl;
72     const IHttpNegotiate2Vtbl     *lpHttpNegotiate2Vtbl;
73     const IInternetBindInfoVtbl   *lpInternetBindInfoVtbl;
74
75     const BSCallbackVtbl          *vtbl;
76
77     LONG ref;
78
79     LPWSTR headers;
80     HGLOBAL post_data;
81     ULONG post_data_len;
82     ULONG readed;
83     DWORD bindf;
84     BOOL bindinfo_ready;
85
86     IMoniker *mon;
87     IBinding *binding;
88
89     HTMLDocumentNode *doc;
90
91     struct list entry;
92 };
93
94 #define NSINSTREAM_THIS(iface) DEFINE_THIS(nsProtocolStream, InputStream, iface)
95
96 static nsresult NSAPI nsInputStream_QueryInterface(nsIInputStream *iface, nsIIDRef riid,
97         void **result)
98 {
99     nsProtocolStream *This = NSINSTREAM_THIS(iface);
100
101     *result = NULL;
102
103     if(IsEqualGUID(&IID_nsISupports, riid)) {
104         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
105         *result  = NSINSTREAM(This);
106     }else if(IsEqualGUID(&IID_nsIInputStream, riid)) {
107         TRACE("(%p)->(IID_nsIInputStream %p)\n", This, result);
108         *result  = NSINSTREAM(This);
109     }
110
111     if(*result) {
112         nsIInputStream_AddRef(NSINSTREAM(This));
113         return NS_OK;
114     }
115
116     WARN("unsupported interface %s\n", debugstr_guid(riid));
117     return NS_NOINTERFACE;
118 }
119
120 static nsrefcnt NSAPI nsInputStream_AddRef(nsIInputStream *iface)
121 {
122     nsProtocolStream *This = NSINSTREAM_THIS(iface);
123     LONG ref = InterlockedIncrement(&This->ref);
124
125     TRACE("(%p) ref=%d\n", This, ref);
126
127     return ref;
128 }
129
130
131 static nsrefcnt NSAPI nsInputStream_Release(nsIInputStream *iface)
132 {
133     nsProtocolStream *This = NSINSTREAM_THIS(iface);
134     LONG ref = InterlockedDecrement(&This->ref);
135
136     TRACE("(%p) ref=%d\n", This, ref);
137
138     if(!ref)
139         heap_free(This);
140
141     return ref;
142 }
143
144 static nsresult NSAPI nsInputStream_Close(nsIInputStream *iface)
145 {
146     nsProtocolStream *This = NSINSTREAM_THIS(iface);
147     FIXME("(%p)\n", This);
148     return NS_ERROR_NOT_IMPLEMENTED;
149 }
150
151 static nsresult NSAPI nsInputStream_Available(nsIInputStream *iface, PRUint32 *_retval)
152 {
153     nsProtocolStream *This = NSINSTREAM_THIS(iface);
154     FIXME("(%p)->(%p)\n", This, _retval);
155     return NS_ERROR_NOT_IMPLEMENTED;
156 }
157
158 static nsresult NSAPI nsInputStream_Read(nsIInputStream *iface, char *aBuf, PRUint32 aCount,
159                                          PRUint32 *_retval)
160 {
161     nsProtocolStream *This = NSINSTREAM_THIS(iface);
162     DWORD read = aCount;
163
164     TRACE("(%p)->(%p %d %p)\n", This, aBuf, aCount, _retval);
165
166     if(read > This->buf_size)
167         read = This->buf_size;
168
169     if(read) {
170         memcpy(aBuf, This->buf, read);
171         if(read < This->buf_size)
172             memmove(This->buf, This->buf+read, This->buf_size-read);
173         This->buf_size -= read;
174     }
175
176     *_retval = read;
177     return NS_OK;
178 }
179
180 static nsresult NSAPI nsInputStream_ReadSegments(nsIInputStream *iface,
181         nsresult (WINAPI *aWriter)(nsIInputStream*,void*,const char*,PRUint32,PRUint32,PRUint32*),
182         void *aClousure, PRUint32 aCount, PRUint32 *_retval)
183 {
184     nsProtocolStream *This = NSINSTREAM_THIS(iface);
185     PRUint32 written = 0;
186     nsresult nsres;
187
188     TRACE("(%p)->(%p %p %d %p)\n", This, aWriter, aClousure, aCount, _retval);
189
190     if(!This->buf_size)
191         return S_OK;
192
193     if(aCount > This->buf_size)
194         aCount = This->buf_size;
195
196     nsres = aWriter(NSINSTREAM(This), aClousure, This->buf, 0, aCount, &written);
197     if(NS_FAILED(nsres))
198         TRACE("aWritter failed: %08x\n", nsres);
199     else if(written != This->buf_size)
200         FIXME("written %d != buf_size %d\n", written, This->buf_size);
201
202     This->buf_size -= written; 
203
204     *_retval = written;
205     return nsres;
206 }
207
208 static nsresult NSAPI nsInputStream_IsNonBlocking(nsIInputStream *iface, PRBool *_retval)
209 {
210     nsProtocolStream *This = NSINSTREAM_THIS(iface);
211     FIXME("(%p)->(%p)\n", This, _retval);
212     return NS_ERROR_NOT_IMPLEMENTED;
213 }
214
215 #undef NSINSTREAM_THIS
216
217 static const nsIInputStreamVtbl nsInputStreamVtbl = {
218     nsInputStream_QueryInterface,
219     nsInputStream_AddRef,
220     nsInputStream_Release,
221     nsInputStream_Close,
222     nsInputStream_Available,
223     nsInputStream_Read,
224     nsInputStream_ReadSegments,
225     nsInputStream_IsNonBlocking
226 };
227
228 static nsProtocolStream *create_nsprotocol_stream(void)
229 {
230     nsProtocolStream *ret = heap_alloc(sizeof(nsProtocolStream));
231
232     ret->lpInputStreamVtbl = &nsInputStreamVtbl;
233     ret->ref = 1;
234     ret->buf_size = 0;
235
236     return ret;
237 }
238
239 #define STATUSCLB_THIS(iface) DEFINE_THIS(BSCallback, BindStatusCallback, iface)
240
241 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
242         REFIID riid, void **ppv)
243 {
244     BSCallback *This = STATUSCLB_THIS(iface);
245
246     *ppv = NULL;
247     if(IsEqualGUID(&IID_IUnknown, riid)) {
248         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
249         *ppv = STATUSCLB(This);
250     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
251         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv);
252         *ppv = STATUSCLB(This);
253     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
254         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
255         *ppv = SERVPROV(This);
256     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
257         TRACE("(%p)->(IID_IHttpNegotiate %p)\n", This, ppv);
258         *ppv = HTTPNEG(This);
259     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
260         TRACE("(%p)->(IID_IHttpNegotiate2 %p)\n", This, ppv);
261         *ppv = HTTPNEG(This);
262     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
263         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
264         *ppv = BINDINFO(This);
265     }
266
267     if(*ppv) {
268         IBindStatusCallback_AddRef(STATUSCLB(This));
269         return S_OK;
270     }
271
272     TRACE("Unsupported riid = %s\n", debugstr_guid(riid));
273     return E_NOINTERFACE;
274 }
275
276 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
277 {
278     BSCallback *This = STATUSCLB_THIS(iface);
279     LONG ref = InterlockedIncrement(&This->ref);
280
281     TRACE("(%p) ref = %d\n", This, ref);
282
283     return ref;
284 }
285
286 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
287 {
288     BSCallback *This = STATUSCLB_THIS(iface);
289     LONG ref = InterlockedDecrement(&This->ref);
290
291     TRACE("(%p) ref = %d\n", This, ref);
292
293     if(!ref) {
294         if(This->post_data)
295             GlobalFree(This->post_data);
296         if(This->mon)
297             IMoniker_Release(This->mon);
298         if(This->binding)
299             IBinding_Release(This->binding);
300         list_remove(&This->entry);
301         heap_free(This->headers);
302
303         This->vtbl->destroy(This);
304     }
305
306     return ref;
307 }
308
309 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
310         DWORD dwReserved, IBinding *pbind)
311 {
312     BSCallback *This = STATUSCLB_THIS(iface);
313
314     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);
315
316     IBinding_AddRef(pbind);
317     This->binding = pbind;
318
319     if(This->doc)
320         list_add_head(&This->doc->bindings, &This->entry);
321
322     return This->vtbl->start_binding(This);
323 }
324
325 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
326 {
327     BSCallback *This = STATUSCLB_THIS(iface);
328     FIXME("(%p)->(%p)\n", This, pnPriority);
329     return E_NOTIMPL;
330 }
331
332 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
333 {
334     BSCallback *This = STATUSCLB_THIS(iface);
335     FIXME("(%p)->(%d)\n", This, reserved);
336     return E_NOTIMPL;
337 }
338
339 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
340         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
341 {
342     BSCallback *This = STATUSCLB_THIS(iface);
343
344     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
345             debugstr_w(szStatusText));
346
347     return This->vtbl->on_progress(This, ulStatusCode, szStatusText);
348 }
349
350 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
351         HRESULT hresult, LPCWSTR szError)
352 {
353     BSCallback *This = STATUSCLB_THIS(iface);
354     HRESULT hres;
355
356     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
357
358     /* NOTE: IE7 calls GetBindResult here */
359
360     hres = This->vtbl->stop_binding(This, hresult);
361
362     if(This->binding) {
363         IBinding_Release(This->binding);
364         This->binding = NULL;
365     }
366
367     list_remove(&This->entry);
368     This->doc = NULL;
369
370     return hres;
371 }
372
373 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
374         DWORD *grfBINDF, BINDINFO *pbindinfo)
375 {
376     BSCallback *This = STATUSCLB_THIS(iface);
377     DWORD size;
378
379     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
380
381     if(!This->bindinfo_ready) {
382         HRESULT hres;
383
384         hres = This->vtbl->init_bindinfo(This);
385         if(FAILED(hres))
386             return hres;
387
388         This->bindinfo_ready = TRUE;
389     }
390
391     *grfBINDF = This->bindf;
392
393     size = pbindinfo->cbSize;
394     memset(pbindinfo, 0, size);
395     pbindinfo->cbSize = size;
396
397     pbindinfo->cbstgmedData = This->post_data_len;
398     pbindinfo->dwCodePage = CP_UTF8;
399     pbindinfo->dwOptions = 0x80000;
400
401     if(This->post_data) {
402         pbindinfo->dwBindVerb = BINDVERB_POST;
403
404         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
405         pbindinfo->stgmedData.u.hGlobal = This->post_data;
406         pbindinfo->stgmedData.pUnkForRelease = (IUnknown*)STATUSCLB(This);
407         IBindStatusCallback_AddRef(STATUSCLB(This));
408     }
409
410     return S_OK;
411 }
412
413 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
414         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
415 {
416     BSCallback *This = STATUSCLB_THIS(iface);
417
418     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
419
420     return This->vtbl->read_data(This, pstgmed->u.pstm);
421 }
422
423 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
424         REFIID riid, IUnknown *punk)
425 {
426     BSCallback *This = STATUSCLB_THIS(iface);
427     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);
428     return E_NOTIMPL;
429 }
430
431 #undef STATUSCLB_THIS
432
433 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
434     BindStatusCallback_QueryInterface,
435     BindStatusCallback_AddRef,
436     BindStatusCallback_Release,
437     BindStatusCallback_OnStartBinding,
438     BindStatusCallback_GetPriority,
439     BindStatusCallback_OnLowResource,
440     BindStatusCallback_OnProgress,
441     BindStatusCallback_OnStopBinding,
442     BindStatusCallback_GetBindInfo,
443     BindStatusCallback_OnDataAvailable,
444     BindStatusCallback_OnObjectAvailable
445 };
446
447 #define HTTPNEG_THIS(iface) DEFINE_THIS(BSCallback, HttpNegotiate2, iface)
448
449 static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
450                                                    REFIID riid, void **ppv)
451 {
452     BSCallback *This = HTTPNEG_THIS(iface);
453     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
454 }
455
456 static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface)
457 {
458     BSCallback *This = HTTPNEG_THIS(iface);
459     return IBindStatusCallback_AddRef(STATUSCLB(This));
460 }
461
462 static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface)
463 {
464     BSCallback *This = HTTPNEG_THIS(iface);
465     return IBindStatusCallback_Release(STATUSCLB(This));
466 }
467
468 static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
469         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
470 {
471     BSCallback *This = HTTPNEG_THIS(iface);
472     HRESULT hres;
473
474     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders),
475           dwReserved, pszAdditionalHeaders);
476
477     *pszAdditionalHeaders = NULL;
478
479     hres = This->vtbl->beginning_transaction(This, pszAdditionalHeaders);
480     if(hres != S_FALSE)
481         return hres;
482
483     if(This->headers) {
484         DWORD size;
485
486         size = (strlenW(This->headers)+1)*sizeof(WCHAR);
487         *pszAdditionalHeaders = CoTaskMemAlloc(size);
488         if(!*pszAdditionalHeaders)
489             return E_OUTOFMEMORY;
490         memcpy(*pszAdditionalHeaders, This->headers, size);
491     }
492
493     return S_OK;
494 }
495
496 static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
497         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
498 {
499     BSCallback *This = HTTPNEG_THIS(iface);
500
501     TRACE("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
502           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
503
504     return This->vtbl->on_response(This, dwResponseCode, szResponseHeaders);
505 }
506
507 static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
508         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
509 {
510     BSCallback *This = HTTPNEG_THIS(iface);
511     FIXME("(%p)->(%p %p %ld)\n", This, pbSecurityId, pcbSecurityId, dwReserved);
512     return E_NOTIMPL;
513 }
514
515 #undef HTTPNEG
516
517 static const IHttpNegotiate2Vtbl HttpNegotiate2Vtbl = {
518     HttpNegotiate_QueryInterface,
519     HttpNegotiate_AddRef,
520     HttpNegotiate_Release,
521     HttpNegotiate_BeginningTransaction,
522     HttpNegotiate_OnResponse,
523     HttpNegotiate_GetRootSecurityId
524 };
525
526 #define BINDINFO_THIS(iface) DEFINE_THIS(BSCallback, InternetBindInfo, iface)
527
528 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
529                                                       REFIID riid, void **ppv)
530 {
531     BSCallback *This = BINDINFO_THIS(iface);
532     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
533 }
534
535 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
536 {
537     BSCallback *This = BINDINFO_THIS(iface);
538     return IBindStatusCallback_AddRef(STATUSCLB(This));
539 }
540
541 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
542 {
543     BSCallback *This = BINDINFO_THIS(iface);
544     return IBindStatusCallback_Release(STATUSCLB(This));
545 }
546
547 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
548                                                    DWORD *grfBINDF, BINDINFO *pbindinfo)
549 {
550     BSCallback *This = BINDINFO_THIS(iface);
551     FIXME("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
552     return E_NOTIMPL;
553 }
554
555 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
556         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
557 {
558     BSCallback *This = BINDINFO_THIS(iface);
559     FIXME("(%p)->(%u %p %u %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
560     return E_NOTIMPL;
561 }
562
563 #undef BINDINFO_THIS
564
565 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
566     InternetBindInfo_QueryInterface,
567     InternetBindInfo_AddRef,
568     InternetBindInfo_Release,
569     InternetBindInfo_GetBindInfo,
570     InternetBindInfo_GetBindString
571 };
572
573 #define SERVPROV_THIS(iface) DEFINE_THIS(BSCallback, ServiceProvider, iface)
574
575 static HRESULT WINAPI BSCServiceProvider_QueryInterface(IServiceProvider *iface,
576                                                         REFIID riid, void **ppv)
577 {
578     BSCallback *This = SERVPROV_THIS(iface);
579     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
580 }
581
582 static ULONG WINAPI BSCServiceProvider_AddRef(IServiceProvider *iface)
583 {
584     BSCallback *This = SERVPROV_THIS(iface);
585     return IBindStatusCallback_AddRef(STATUSCLB(This));
586 }
587
588 static ULONG WINAPI BSCServiceProvider_Release(IServiceProvider *iface)
589 {
590     BSCallback *This = SERVPROV_THIS(iface);
591     return IBindStatusCallback_Release(STATUSCLB(This));
592 }
593
594 static HRESULT WINAPI BSCServiceProvider_QueryService(IServiceProvider *iface,
595         REFGUID guidService, REFIID riid, void **ppv)
596 {
597     BSCallback *This = SERVPROV_THIS(iface);
598     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
599     return E_NOINTERFACE;
600 }
601
602 #undef SERVPROV_THIS
603
604 static const IServiceProviderVtbl ServiceProviderVtbl = {
605     BSCServiceProvider_QueryInterface,
606     BSCServiceProvider_AddRef,
607     BSCServiceProvider_Release,
608     BSCServiceProvider_QueryService
609 };
610
611 static void init_bscallback(BSCallback *This, const BSCallbackVtbl *vtbl, IMoniker *mon, DWORD bindf)
612 {
613     This->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
614     This->lpServiceProviderVtbl    = &ServiceProviderVtbl;
615     This->lpHttpNegotiate2Vtbl     = &HttpNegotiate2Vtbl;
616     This->lpInternetBindInfoVtbl   = &InternetBindInfoVtbl;
617     This->vtbl = vtbl;
618     This->ref = 1;
619     This->bindf = bindf;
620
621     list_init(&This->entry);
622
623     if(mon)
624         IMoniker_AddRef(mon);
625     This->mon = mon;
626 }
627
628 /* Calls undocumented 84 cmd of CGID_ShellDocView */
629 static void call_docview_84(HTMLDocumentObj *doc)
630 {
631     IOleCommandTarget *olecmd;
632     VARIANT var;
633     HRESULT hres;
634
635     if(!doc->client)
636         return;
637
638     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
639     if(FAILED(hres))
640         return;
641
642     VariantInit(&var);
643     hres = IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 84, 0, NULL, &var);
644     IOleCommandTarget_Release(olecmd);
645     if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
646         FIXME("handle result\n");
647 }
648
649 static HRESULT parse_headers(const WCHAR *headers, struct list *headers_list)
650 {
651     const WCHAR *header, *header_end, *colon, *value;
652     HRESULT hres;
653
654     header = headers;
655     while(*header) {
656         if(header[0] == '\r' && header[1] == '\n' && !header[2])
657             break;
658         for(colon = header; *colon && *colon != ':' && *colon != '\r'; colon++);
659         if(*colon != ':')
660             return E_FAIL;
661
662         value = colon+1;
663         while(*value == ' ')
664             value++;
665         if(!*value)
666             return E_FAIL;
667
668         for(header_end = value+1; *header_end && *header_end != '\r'; header_end++);
669
670         hres = set_http_header(headers_list, header, colon-header, value, header_end-value);
671         if(FAILED(hres))
672             return hres;
673
674         header = header_end;
675         if(header[0] == '\r' && header[1] == '\n')
676             header += 2;
677     }
678
679     return S_OK;
680 }
681
682 static HRESULT read_post_data_stream(nsIInputStream *stream, HGLOBAL *post_data,
683         ULONG *post_data_len)
684 {
685     PRUint32 data_len = 0, available = 0;
686     char *data;
687     nsresult nsres;
688
689     nsres =  nsIInputStream_Available(stream, &available);
690     if(NS_FAILED(nsres))
691         return E_FAIL;
692
693     data = GlobalAlloc(0, available+1);
694     if(!data)
695         return E_OUTOFMEMORY;
696
697     nsres = nsIInputStream_Read(stream, data, available, &data_len);
698     if(NS_FAILED(nsres)) {
699         GlobalFree(data);
700         return E_FAIL;
701     }
702
703     data[data_len] = 0;
704     *post_data = data;
705     *post_data_len = data_len;
706     return S_OK;
707 }
708
709 HRESULT start_binding(HTMLWindow *window, HTMLDocumentNode *doc, BSCallback *bscallback, IBindCtx *bctx)
710 {
711     IStream *str = NULL;
712     HRESULT hres;
713
714     bscallback->doc = doc;
715
716     /* NOTE: IE7 calls IsSystemMoniker here*/
717
718     if(window) {
719         if(bscallback->mon != window->mon)
720             set_current_mon(window, bscallback->mon);
721         call_docview_84(window->doc_obj);
722     }
723
724     if(bctx) {
725         RegisterBindStatusCallback(bctx, STATUSCLB(bscallback), NULL, 0);
726         IBindCtx_AddRef(bctx);
727     }else {
728         hres = CreateAsyncBindCtx(0, STATUSCLB(bscallback), NULL, &bctx);
729         if(FAILED(hres)) {
730             WARN("CreateAsyncBindCtx failed: %08x\n", hres);
731             bscallback->vtbl->stop_binding(bscallback, hres);
732             return hres;
733         }
734     }
735
736     hres = IMoniker_BindToStorage(bscallback->mon, bctx, NULL, &IID_IStream, (void**)&str);
737     IBindCtx_Release(bctx);
738     if(FAILED(hres)) {
739         WARN("BindToStorage failed: %08x\n", hres);
740         bscallback->vtbl->stop_binding(bscallback, hres);
741         return hres;
742     }
743
744     if(str)
745         IStream_Release(str);
746
747     IMoniker_Release(bscallback->mon);
748     bscallback->mon = NULL;
749
750     return S_OK;
751 }
752
753 typedef struct {
754     BSCallback bsc;
755
756     DWORD size;
757     BYTE *buf;
758     HRESULT hres;
759 } BufferBSC;
760
761 #define BUFFERBSC_THIS(bsc) ((BufferBSC*) bsc)
762
763 static void BufferBSC_destroy(BSCallback *bsc)
764 {
765     BufferBSC *This = BUFFERBSC_THIS(bsc);
766
767     heap_free(This->buf);
768     heap_free(This);
769 }
770
771 static HRESULT BufferBSC_init_bindinfo(BSCallback *bsc)
772 {
773     return S_OK;
774 }
775
776 static HRESULT BufferBSC_start_binding(BSCallback *bsc)
777 {
778     return S_OK;
779 }
780
781 static HRESULT BufferBSC_stop_binding(BSCallback *bsc, HRESULT result)
782 {
783     BufferBSC *This = BUFFERBSC_THIS(bsc);
784
785     This->hres = result;
786
787     if(FAILED(result)) {
788         heap_free(This->buf);
789         This->buf = NULL;
790         This->size = 0;
791     }
792
793     return S_OK;
794 }
795
796 static HRESULT BufferBSC_read_data(BSCallback *bsc, IStream *stream)
797 {
798     BufferBSC *This = BUFFERBSC_THIS(bsc);
799     DWORD readed;
800     HRESULT hres;
801
802     if(!This->buf) {
803         This->size = 128;
804         This->buf = heap_alloc(This->size);
805     }
806
807     do {
808         if(This->bsc.readed == This->size) {
809             This->size <<= 1;
810             This->buf = heap_realloc(This->buf, This->size);
811         }
812
813         readed = 0;
814         hres = IStream_Read(stream, This->buf+This->bsc.readed, This->size-This->bsc.readed, &readed);
815         This->bsc.readed += readed;
816     }while(hres == S_OK);
817
818     return S_OK;
819 }
820
821 static HRESULT BufferBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
822 {
823     return S_OK;
824 }
825
826 static HRESULT BufferBSC_on_response(BSCallback *bsc, DWORD response_code,
827         LPCWSTR response_headers)
828 {
829     return S_OK;
830 }
831
832 static HRESULT BufferBSC_beginning_transaction(BSCallback *bsc, WCHAR **additional_headers)
833 {
834     return S_FALSE;
835 }
836
837 #undef BUFFERBSC_THIS
838
839 static const BSCallbackVtbl BufferBSCVtbl = {
840     BufferBSC_destroy,
841     BufferBSC_init_bindinfo,
842     BufferBSC_start_binding,
843     BufferBSC_stop_binding,
844     BufferBSC_read_data,
845     BufferBSC_on_progress,
846     BufferBSC_on_response,
847     BufferBSC_beginning_transaction
848 };
849
850
851 static BufferBSC *create_bufferbsc(IMoniker *mon)
852 {
853     BufferBSC *ret = heap_alloc_zero(sizeof(*ret));
854
855     init_bscallback(&ret->bsc, &BufferBSCVtbl, mon, 0);
856     ret->hres = E_FAIL;
857
858     return ret;
859 }
860
861 HRESULT bind_mon_to_buffer(HTMLDocumentNode *doc, IMoniker *mon, void **buf, DWORD *size)
862 {
863     BufferBSC *bsc = create_bufferbsc(mon);
864     HRESULT hres;
865
866     *buf = NULL;
867
868     hres = start_binding(NULL, doc, &bsc->bsc, NULL);
869     if(SUCCEEDED(hres)) {
870         hres = bsc->hres;
871         if(SUCCEEDED(hres)) {
872             *buf = bsc->buf;
873             bsc->buf = NULL;
874             *size = bsc->bsc.readed;
875             bsc->size = 0;
876         }
877     }
878
879     IBindStatusCallback_Release(STATUSCLB(&bsc->bsc));
880
881     return hres;
882 }
883
884 struct nsChannelBSC {
885     BSCallback bsc;
886
887     HTMLWindow *window;
888
889     nsChannel *nschannel;
890     nsIStreamListener *nslistener;
891     nsISupports *nscontext;
892
893     nsProtocolStream *nsstream;
894 };
895
896 static HRESULT on_start_nsrequest(nsChannelBSC *This)
897 {
898     nsresult nsres;
899
900     /* FIXME: it's needed for http connections from BindToObject. */
901     if(!This->nschannel->response_status)
902         This->nschannel->response_status = 200;
903
904     nsres = nsIStreamListener_OnStartRequest(This->nslistener,
905             (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext);
906     if(NS_FAILED(nsres)) {
907         FIXME("OnStartRequest failed: %08x\n", nsres);
908         return E_FAIL;
909     }
910
911     if(This->window) {
912         update_window_doc(This->window);
913         if(This->window->doc != This->bsc.doc)
914             This->bsc.doc = This->window->doc;
915         if(This->window->readystate != READYSTATE_LOADING)
916             set_ready_state(This->window, READYSTATE_LOADING);
917     }
918
919     return S_OK;
920 }
921
922 static void on_stop_nsrequest(nsChannelBSC *This, HRESULT result)
923 {
924     nsresult nsres, request_result;
925
926     switch(result) {
927     case S_OK:
928         request_result = NS_OK;
929         break;
930     case E_ABORT:
931         request_result = NS_BINDING_ABORTED;
932         break;
933     default:
934         request_result = NS_ERROR_FAILURE;
935     }
936
937     if(!This->bsc.readed && SUCCEEDED(result)) {
938         TRACE("No data read! Calling OnStartRequest\n");
939         on_start_nsrequest(This);
940     }
941
942     if(This->nslistener) {
943         nsres = nsIStreamListener_OnStopRequest(This->nslistener,
944                  (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext, request_result);
945         if(NS_FAILED(nsres))
946             WARN("OnStopRequet failed: %08x\n", nsres);
947     }
948
949     if(This->nschannel->load_group) {
950         nsres = nsILoadGroup_RemoveRequest(This->nschannel->load_group,
951                 (nsIRequest*)NSCHANNEL(This->nschannel), NULL, request_result);
952         if(NS_FAILED(nsres))
953             ERR("RemoveRequest failed: %08x\n", nsres);
954     }
955 }
956
957 static HRESULT read_stream_data(nsChannelBSC *This, IStream *stream)
958 {
959     DWORD read;
960     nsresult nsres;
961     HRESULT hres;
962
963     if(!This->nslistener) {
964         BYTE buf[1024];
965
966         do {
967             read = 0;
968             hres = IStream_Read(stream, buf, sizeof(buf), &read);
969         }while(hres == S_OK && read);
970
971         return S_OK;
972     }
973
974     if(!This->nsstream)
975         This->nsstream = create_nsprotocol_stream();
976
977     do {
978         read = 0;
979         hres = IStream_Read(stream, This->nsstream->buf+This->nsstream->buf_size,
980                 sizeof(This->nsstream->buf)-This->nsstream->buf_size, &read);
981         if(!read)
982             break;
983
984         This->nsstream->buf_size += read;
985
986         if(!This->bsc.readed) {
987             if(This->nsstream->buf_size >= 2
988                && (BYTE)This->nsstream->buf[0] == 0xff
989                && (BYTE)This->nsstream->buf[1] == 0xfe)
990                 This->nschannel->charset = heap_strdupA(UTF16_STR);
991
992             if(!This->nschannel->content_type) {
993                 WCHAR *mime;
994
995                 hres = FindMimeFromData(NULL, NULL, This->nsstream->buf, This->nsstream->buf_size, NULL, 0, &mime, 0);
996                 if(FAILED(hres))
997                     return hres;
998
999                 TRACE("Found MIME %s\n", debugstr_w(mime));
1000
1001                 This->nschannel->content_type = heap_strdupWtoA(mime);
1002                 CoTaskMemFree(mime);
1003                 if(!This->nschannel->content_type)
1004                     return E_OUTOFMEMORY;
1005             }
1006
1007             on_start_nsrequest(This);
1008         }
1009
1010         This->bsc.readed += This->nsstream->buf_size;
1011
1012         nsres = nsIStreamListener_OnDataAvailable(This->nslistener,
1013                 (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext,
1014                 NSINSTREAM(This->nsstream), This->bsc.readed-This->nsstream->buf_size,
1015                 This->nsstream->buf_size);
1016         if(NS_FAILED(nsres))
1017             ERR("OnDataAvailable failed: %08x\n", nsres);
1018
1019         if(This->nsstream->buf_size == sizeof(This->nsstream->buf)) {
1020             ERR("buffer is full\n");
1021             break;
1022         }
1023     }while(hres == S_OK);
1024
1025     return S_OK;
1026 }
1027
1028 #define NSCHANNELBSC_THIS(bsc) ((nsChannelBSC*) bsc)
1029
1030 static void nsChannelBSC_destroy(BSCallback *bsc)
1031 {
1032     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1033
1034     if(This->nschannel)
1035         nsIChannel_Release(NSCHANNEL(This->nschannel));
1036     if(This->nslistener)
1037         nsIStreamListener_Release(This->nslistener);
1038     if(This->nscontext)
1039         nsISupports_Release(This->nscontext);
1040     if(This->nsstream)
1041         nsIInputStream_Release(NSINSTREAM(This->nsstream));
1042     heap_free(This);
1043 }
1044
1045 static HRESULT nsChannelBSC_start_binding(BSCallback *bsc)
1046 {
1047     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1048
1049     if(This->window)
1050         This->window->doc->skip_mutation_notif = FALSE;
1051
1052     return S_OK;
1053 }
1054
1055 static HRESULT nsChannelBSC_init_bindinfo(BSCallback *bsc)
1056 {
1057     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1058     HRESULT hres;
1059
1060     if(This->nschannel && This->nschannel->post_data_stream) {
1061         hres = read_post_data_stream(This->nschannel->post_data_stream,
1062                 &This->bsc.post_data, &This->bsc.post_data_len);
1063         if(FAILED(hres))
1064             return hres;
1065
1066         TRACE("post_data = %s\n", debugstr_an(This->bsc.post_data, This->bsc.post_data_len));
1067     }
1068
1069     return S_OK;
1070 }
1071
1072 typedef struct {
1073     task_t header;
1074     nsChannelBSC *bsc;
1075 } stop_request_task_t;
1076
1077 static void stop_request_proc(task_t *_task)
1078 {
1079     stop_request_task_t *task = (stop_request_task_t*)_task;
1080
1081     TRACE("(%p)\n", task->bsc);
1082
1083     on_stop_nsrequest(task->bsc, S_OK);
1084     IBindStatusCallback_Release(STATUSCLB(&task->bsc->bsc));
1085 }
1086
1087 static HRESULT async_stop_request(nsChannelBSC *This)
1088 {
1089     stop_request_task_t *task;
1090
1091     task = heap_alloc(sizeof(*task));
1092     if(!task)
1093         return E_OUTOFMEMORY;
1094
1095     IBindStatusCallback_AddRef(STATUSCLB(&This->bsc));
1096     task->bsc = This;
1097     push_task(&task->header, stop_request_proc, This->bsc.doc->basedoc.doc_obj->basedoc.task_magic);
1098     return S_OK;
1099 }
1100
1101 static HRESULT nsChannelBSC_stop_binding(BSCallback *bsc, HRESULT result)
1102 {
1103     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1104
1105     if(This->window && SUCCEEDED(result)) {
1106         result = async_stop_request(This);
1107         if(SUCCEEDED(result))
1108            return S_OK;
1109     }
1110
1111     on_stop_nsrequest(This, result);
1112     return S_OK;
1113 }
1114
1115 static HRESULT nsChannelBSC_read_data(BSCallback *bsc, IStream *stream)
1116 {
1117     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1118
1119     return read_stream_data(This, stream);
1120 }
1121
1122 static HRESULT nsChannelBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
1123 {
1124     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1125
1126     switch(status_code) {
1127     case BINDSTATUS_MIMETYPEAVAILABLE:
1128         if(!This->nschannel)
1129             return S_OK;
1130
1131         heap_free(This->nschannel->content_type);
1132         This->nschannel->content_type = heap_strdupWtoA(status_text);
1133         break;
1134     case BINDSTATUS_REDIRECTING:
1135         TRACE("redirect to %s\n", debugstr_w(status_text));
1136
1137         /* FIXME: We should find a better way to handle this */
1138         set_wine_url(This->nschannel->uri, status_text);
1139     }
1140
1141     return S_OK;
1142 }
1143
1144 static HRESULT nsChannelBSC_on_response(BSCallback *bsc, DWORD response_code,
1145         LPCWSTR response_headers)
1146 {
1147     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1148     HRESULT hres;
1149
1150     This->nschannel->response_status = response_code;
1151
1152     if(response_headers) {
1153         const WCHAR *headers;
1154
1155         headers = strchrW(response_headers, '\r');
1156         if(headers && headers[1] == '\n') {
1157             headers += 2;
1158             hres = parse_headers(headers, &This->nschannel->response_headers);
1159             if(FAILED(hres)) {
1160                 WARN("parsing headers failed: %08x\n", hres);
1161                 return hres;
1162             }
1163         }
1164     }
1165
1166     return S_OK;
1167 }
1168
1169 static HRESULT nsChannelBSC_beginning_transaction(BSCallback *bsc, WCHAR **additional_headers)
1170 {
1171     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1172     http_header_t *iter;
1173     DWORD len = 0;
1174     WCHAR *ptr;
1175
1176     static const WCHAR content_lengthW[] =
1177         {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
1178
1179     if(!This->nschannel)
1180         return S_FALSE;
1181
1182     LIST_FOR_EACH_ENTRY(iter, &This->nschannel->request_headers, http_header_t, entry) {
1183         if(strcmpW(iter->header, content_lengthW))
1184             len += strlenW(iter->header) + 2 /* ": " */ + strlenW(iter->data) + 2 /* "\r\n" */;
1185     }
1186
1187     if(!len)
1188         return S_OK;
1189
1190     *additional_headers = ptr = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
1191     if(!ptr)
1192         return E_OUTOFMEMORY;
1193
1194     LIST_FOR_EACH_ENTRY(iter, &This->nschannel->request_headers, http_header_t, entry) {
1195         if(!strcmpW(iter->header, content_lengthW))
1196             continue;
1197
1198         len = strlenW(iter->header);
1199         memcpy(ptr, iter->header, len*sizeof(WCHAR));
1200         ptr += len;
1201
1202         *ptr++ = ':';
1203         *ptr++ = ' ';
1204
1205         len = strlenW(iter->data);
1206         memcpy(ptr, iter->data, len*sizeof(WCHAR));
1207         ptr += len;
1208
1209         *ptr++ = '\r';
1210         *ptr++ = '\n';
1211     }
1212
1213     *ptr = 0;
1214
1215     return S_OK;
1216 }
1217
1218 #undef NSCHANNELBSC_THIS
1219
1220 static const BSCallbackVtbl nsChannelBSCVtbl = {
1221     nsChannelBSC_destroy,
1222     nsChannelBSC_init_bindinfo,
1223     nsChannelBSC_start_binding,
1224     nsChannelBSC_stop_binding,
1225     nsChannelBSC_read_data,
1226     nsChannelBSC_on_progress,
1227     nsChannelBSC_on_response,
1228     nsChannelBSC_beginning_transaction
1229 };
1230
1231 HRESULT create_channelbsc(IMoniker *mon, WCHAR *headers, BYTE *post_data, DWORD post_data_size, nsChannelBSC **retval)
1232 {
1233     nsChannelBSC *ret;
1234
1235     ret = heap_alloc_zero(sizeof(*ret));
1236     if(!ret)
1237         return E_OUTOFMEMORY;
1238
1239     init_bscallback(&ret->bsc, &nsChannelBSCVtbl, mon, BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA);
1240
1241     if(headers) {
1242         ret->bsc.headers = heap_strdupW(headers);
1243         if(!ret->bsc.headers) {
1244             IBindStatusCallback_Release(STATUSCLB(&ret->bsc));
1245             return E_OUTOFMEMORY;
1246         }
1247     }
1248
1249     if(post_data) {
1250         ret->bsc.post_data = GlobalAlloc(0, post_data_size);
1251         if(!ret->bsc.post_data) {
1252             heap_free(ret->bsc.headers);
1253             IBindStatusCallback_Release(STATUSCLB(&ret->bsc));
1254             return E_OUTOFMEMORY;
1255         }
1256
1257         memcpy(ret->bsc.post_data, post_data, post_data_size);
1258         ret->bsc.post_data_len = post_data_size;
1259     }
1260
1261     *retval = ret;
1262     return S_OK;
1263 }
1264
1265 IMoniker *get_channelbsc_mon(nsChannelBSC *This)
1266 {
1267     if(This->bsc.mon)
1268         IMoniker_AddRef(This->bsc.mon);
1269     return This->bsc.mon;
1270 }
1271
1272 void set_window_bscallback(HTMLWindow *window, nsChannelBSC *callback)
1273 {
1274     if(window->bscallback) {
1275         if(window->bscallback->bsc.binding)
1276             IBinding_Abort(window->bscallback->bsc.binding);
1277         window->bscallback->bsc.doc = NULL;
1278         window->bscallback->window = NULL;
1279         IBindStatusCallback_Release(STATUSCLB(&window->bscallback->bsc));
1280     }
1281
1282     window->bscallback = callback;
1283
1284     if(callback) {
1285         callback->window = window;
1286         IBindStatusCallback_AddRef(STATUSCLB(&callback->bsc));
1287         callback->bsc.doc = window->doc;
1288     }
1289 }
1290
1291 typedef struct {
1292     task_t header;
1293     HTMLWindow *window;
1294     nsChannelBSC *bscallback;
1295 } start_doc_binding_task_t;
1296
1297 static void start_doc_binding_proc(task_t *_task)
1298 {
1299     start_doc_binding_task_t *task = (start_doc_binding_task_t*)_task;
1300
1301     start_binding(task->window, NULL, (BSCallback*)task->bscallback, NULL);
1302     IBindStatusCallback_Release(STATUSCLB(&task->bscallback->bsc));
1303 }
1304
1305 HRESULT async_start_doc_binding(HTMLWindow *window, nsChannelBSC *bscallback)
1306 {
1307     start_doc_binding_task_t *task;
1308
1309     task = heap_alloc(sizeof(start_doc_binding_task_t));
1310     if(!task)
1311         return E_OUTOFMEMORY;
1312
1313     task->window = window;
1314     task->bscallback = bscallback;
1315     IBindStatusCallback_AddRef(STATUSCLB(&bscallback->bsc));
1316
1317     push_task(&task->header, start_doc_binding_proc, window->task_magic);
1318     return S_OK;
1319 }
1320
1321 void abort_document_bindings(HTMLDocumentNode *doc)
1322 {
1323     BSCallback *iter;
1324
1325     LIST_FOR_EACH_ENTRY(iter, &doc->bindings, BSCallback, entry) {
1326         if(iter->binding)
1327             IBinding_Abort(iter->binding);
1328         iter->doc = NULL;
1329         list_remove(&iter->entry);
1330     }
1331 }
1332
1333 HRESULT channelbsc_load_stream(nsChannelBSC *bscallback, IStream *stream)
1334 {
1335     HRESULT hres = S_OK;
1336
1337     if(!bscallback->nschannel) {
1338         ERR("NULL nschannel\n");
1339         return E_FAIL;
1340     }
1341
1342     bscallback->nschannel->content_type = heap_strdupA("text/html");
1343     if(!bscallback->nschannel->content_type)
1344         return E_OUTOFMEMORY;
1345
1346     if(stream)
1347         hres = read_stream_data(bscallback, stream);
1348     if(SUCCEEDED(hres))
1349         hres = async_stop_request(bscallback);
1350     if(FAILED(hres))
1351         IBindStatusCallback_OnStopBinding(STATUSCLB(&bscallback->bsc), hres, ERROR_SUCCESS);
1352
1353     return hres;
1354 }
1355
1356 void channelbsc_set_channel(nsChannelBSC *This, nsChannel *channel, nsIStreamListener *listener, nsISupports *context)
1357 {
1358     nsIChannel_AddRef(NSCHANNEL(channel));
1359     This->nschannel = channel;
1360
1361     nsIStreamListener_AddRef(listener);
1362     This->nslistener = listener;
1363
1364     if(context) {
1365         nsISupports_AddRef(context);
1366         This->nscontext = context;
1367     }
1368
1369     if(This->bsc.headers) {
1370         HRESULT hres;
1371
1372         hres = parse_headers(This->bsc.headers, &channel->request_headers);
1373         heap_free(This->bsc.headers);
1374         This->bsc.headers = NULL;
1375         if(FAILED(hres))
1376             WARN("parse_headers failed: %08x\n", hres);
1377     }
1378 }
1379
1380 HRESULT hlink_frame_navigate(HTMLDocument *doc, LPCWSTR url,
1381         nsIInputStream *post_data_stream, DWORD hlnf, BOOL *cancel)
1382 {
1383     IHlinkFrame *hlink_frame;
1384     nsChannelBSC *callback;
1385     IServiceProvider *sp;
1386     IBindCtx *bindctx;
1387     IMoniker *mon;
1388     IHlink *hlink;
1389     HRESULT hres;
1390
1391     *cancel = FALSE;
1392
1393     hres = IOleClientSite_QueryInterface(doc->doc_obj->client, &IID_IServiceProvider,
1394             (void**)&sp);
1395     if(FAILED(hres))
1396         return S_OK;
1397
1398     hres = IServiceProvider_QueryService(sp, &IID_IHlinkFrame, &IID_IHlinkFrame,
1399             (void**)&hlink_frame);
1400     IServiceProvider_Release(sp);
1401     if(FAILED(hres))
1402         return S_OK;
1403
1404     hres = create_channelbsc(NULL, NULL, NULL, 0, &callback);
1405     if(FAILED(hres)) {
1406         IHlinkFrame_Release(hlink_frame);
1407         return hres;
1408     }
1409
1410     if(post_data_stream) {
1411         read_post_data_stream(post_data_stream, &callback->bsc.post_data, &callback->bsc.post_data_len);
1412         TRACE("post_data = %s\n", debugstr_an(callback->bsc.post_data, callback->bsc.post_data_len));
1413     }
1414
1415     hres = CreateAsyncBindCtx(0, STATUSCLB(&callback->bsc), NULL, &bindctx);
1416     if(SUCCEEDED(hres))
1417         hres = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER,
1418                 &IID_IHlink, (LPVOID*)&hlink);
1419
1420     if(SUCCEEDED(hres))
1421         hres = CreateURLMoniker(NULL, url, &mon);
1422
1423     if(SUCCEEDED(hres)) {
1424         IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET, mon, NULL);
1425
1426         if(hlnf & HLNF_OPENINNEWWINDOW) {
1427             static const WCHAR wszBlank[] = {'_','b','l','a','n','k',0};
1428             IHlink_SetTargetFrameName(hlink, wszBlank); /* FIXME */
1429         }
1430
1431         hres = IHlinkFrame_Navigate(hlink_frame, hlnf, bindctx, STATUSCLB(&callback->bsc), hlink);
1432         IMoniker_Release(mon);
1433         *cancel = hres == S_OK;
1434         hres = S_OK;
1435     }
1436
1437     IHlinkFrame_Release(hlink_frame);
1438     IBindCtx_Release(bindctx);
1439     IBindStatusCallback_Release(STATUSCLB(&callback->bsc));
1440     return hres;
1441 }
1442
1443 HRESULT navigate_url(HTMLWindow *window, const WCHAR *new_url, const WCHAR *base_url)
1444 {
1445     WCHAR url[INTERNET_MAX_URL_LENGTH];
1446     nsWineURI *uri;
1447     HRESULT hres;
1448
1449     if(!new_url) {
1450         *url = 0;
1451     }else if(base_url) {
1452         DWORD len = 0;
1453
1454         hres = CoInternetCombineUrl(base_url, new_url, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO,
1455                 url, sizeof(url)/sizeof(WCHAR), &len, 0);
1456         if(FAILED(hres))
1457             return hres;
1458     }else {
1459         strcpyW(url, new_url);
1460     }
1461
1462     if(window->doc_obj && window->doc_obj->hostui) {
1463         OLECHAR *translated_url = NULL;
1464
1465         hres = IDocHostUIHandler_TranslateUrl(window->doc_obj->hostui, 0, url,
1466                 &translated_url);
1467         if(hres == S_OK) {
1468             TRACE("%08x %s -> %s\n", hres, debugstr_w(url), debugstr_w(translated_url));
1469             strcpyW(url, translated_url);
1470             CoTaskMemFree(translated_url);
1471         }
1472     }
1473
1474     if(window->doc_obj && window == window->doc_obj->basedoc.window) {
1475         BOOL cancel;
1476
1477         hres = hlink_frame_navigate(&window->doc->basedoc, url, NULL, 0, &cancel);
1478         if(FAILED(hres))
1479             return hres;
1480
1481         if(cancel) {
1482             TRACE("Navigation handled by hlink frame\n");
1483             return S_OK;
1484         }
1485     }
1486
1487     hres = create_doc_uri(window, url, &uri);
1488     if(FAILED(hres))
1489         return hres;
1490
1491     hres = load_nsuri(window, uri, NULL, LOAD_FLAGS_NONE);
1492     nsISupports_Release((nsISupports*)uri);
1493     return hres;
1494 }