mshtml: Added noscript tag handling 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 #include <assert.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winreg.h"
32 #include "ole2.h"
33 #include "hlguids.h"
34 #include "shlguid.h"
35 #include "wininet.h"
36 #include "shlwapi.h"
37 #include "htiface.h"
38 #include "shdeprecated.h"
39
40 #include "wine/debug.h"
41
42 #include "mshtml_private.h"
43 #include "binding.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
46
47 #define CONTENT_LENGTH "Content-Length"
48 #define UTF8_STR "utf-8"
49 #define UTF16_STR "utf-16"
50
51 static const WCHAR emptyW[] = {0};
52 static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0};
53
54 enum {
55     BOM_NONE,
56     BOM_UTF8,
57     BOM_UTF16
58 };
59
60 struct nsProtocolStream {
61     nsIInputStream nsIInputStream_iface;
62
63     LONG ref;
64
65     char buf[1024];
66     DWORD buf_size;
67 };
68
69 struct BSCallbackVtbl {
70     void (*destroy)(BSCallback*);
71     HRESULT (*init_bindinfo)(BSCallback*);
72     HRESULT (*start_binding)(BSCallback*);
73     HRESULT (*stop_binding)(BSCallback*,HRESULT);
74     HRESULT (*read_data)(BSCallback*,IStream*);
75     HRESULT (*on_progress)(BSCallback*,ULONG,LPCWSTR);
76     HRESULT (*on_response)(BSCallback*,DWORD,LPCWSTR);
77     HRESULT (*beginning_transaction)(BSCallback*,WCHAR**);
78 };
79
80 static inline nsProtocolStream *impl_from_nsIInputStream(nsIInputStream *iface)
81 {
82     return CONTAINING_RECORD(iface, nsProtocolStream, nsIInputStream_iface);
83 }
84
85 static nsresult NSAPI nsInputStream_QueryInterface(nsIInputStream *iface, nsIIDRef riid,
86         void **result)
87 {
88     nsProtocolStream *This = impl_from_nsIInputStream(iface);
89
90     *result = NULL;
91
92     if(IsEqualGUID(&IID_nsISupports, riid)) {
93         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
94         *result  = &This->nsIInputStream_iface;
95     }else if(IsEqualGUID(&IID_nsIInputStream, riid)) {
96         TRACE("(%p)->(IID_nsIInputStream %p)\n", This, result);
97         *result  = &This->nsIInputStream_iface;
98     }
99
100     if(*result) {
101         nsIInputStream_AddRef(&This->nsIInputStream_iface);
102         return NS_OK;
103     }
104
105     WARN("unsupported interface %s\n", debugstr_guid(riid));
106     return NS_NOINTERFACE;
107 }
108
109 static nsrefcnt NSAPI nsInputStream_AddRef(nsIInputStream *iface)
110 {
111     nsProtocolStream *This = impl_from_nsIInputStream(iface);
112     LONG ref = InterlockedIncrement(&This->ref);
113
114     TRACE("(%p) ref=%d\n", This, ref);
115
116     return ref;
117 }
118
119
120 static nsrefcnt NSAPI nsInputStream_Release(nsIInputStream *iface)
121 {
122     nsProtocolStream *This = impl_from_nsIInputStream(iface);
123     LONG ref = InterlockedDecrement(&This->ref);
124
125     TRACE("(%p) ref=%d\n", This, ref);
126
127     if(!ref)
128         heap_free(This);
129
130     return ref;
131 }
132
133 static nsresult NSAPI nsInputStream_Close(nsIInputStream *iface)
134 {
135     nsProtocolStream *This = impl_from_nsIInputStream(iface);
136     FIXME("(%p)\n", This);
137     return NS_ERROR_NOT_IMPLEMENTED;
138 }
139
140 static nsresult NSAPI nsInputStream_Available(nsIInputStream *iface, PRUint32 *_retval)
141 {
142     nsProtocolStream *This = impl_from_nsIInputStream(iface);
143     FIXME("(%p)->(%p)\n", This, _retval);
144     return NS_ERROR_NOT_IMPLEMENTED;
145 }
146
147 static nsresult NSAPI nsInputStream_Read(nsIInputStream *iface, char *aBuf, PRUint32 aCount,
148                                          PRUint32 *_retval)
149 {
150     nsProtocolStream *This = impl_from_nsIInputStream(iface);
151     DWORD read = aCount;
152
153     TRACE("(%p)->(%p %d %p)\n", This, aBuf, aCount, _retval);
154
155     if(read > This->buf_size)
156         read = This->buf_size;
157
158     if(read) {
159         memcpy(aBuf, This->buf, read);
160         if(read < This->buf_size)
161             memmove(This->buf, This->buf+read, This->buf_size-read);
162         This->buf_size -= read;
163     }
164
165     *_retval = read;
166     return NS_OK;
167 }
168
169 static nsresult NSAPI nsInputStream_ReadSegments(nsIInputStream *iface,
170         nsresult (WINAPI *aWriter)(nsIInputStream*,void*,const char*,PRUint32,PRUint32,PRUint32*),
171         void *aClousure, PRUint32 aCount, PRUint32 *_retval)
172 {
173     nsProtocolStream *This = impl_from_nsIInputStream(iface);
174     PRUint32 written = 0;
175     nsresult nsres;
176
177     TRACE("(%p)->(%p %p %d %p)\n", This, aWriter, aClousure, aCount, _retval);
178
179     if(!This->buf_size)
180         return S_OK;
181
182     if(aCount > This->buf_size)
183         aCount = This->buf_size;
184
185     nsres = aWriter(&This->nsIInputStream_iface, aClousure, This->buf, 0, aCount, &written);
186     if(NS_FAILED(nsres))
187         TRACE("aWritter failed: %08x\n", nsres);
188     else if(written != This->buf_size)
189         FIXME("written %d != buf_size %d\n", written, This->buf_size);
190
191     This->buf_size -= written; 
192
193     *_retval = written;
194     return nsres;
195 }
196
197 static nsresult NSAPI nsInputStream_IsNonBlocking(nsIInputStream *iface, cpp_bool *_retval)
198 {
199     nsProtocolStream *This = impl_from_nsIInputStream(iface);
200     FIXME("(%p)->(%p)\n", This, _retval);
201     return NS_ERROR_NOT_IMPLEMENTED;
202 }
203
204 static const nsIInputStreamVtbl nsInputStreamVtbl = {
205     nsInputStream_QueryInterface,
206     nsInputStream_AddRef,
207     nsInputStream_Release,
208     nsInputStream_Close,
209     nsInputStream_Available,
210     nsInputStream_Read,
211     nsInputStream_ReadSegments,
212     nsInputStream_IsNonBlocking
213 };
214
215 static nsProtocolStream *create_nsprotocol_stream(void)
216 {
217     nsProtocolStream *ret = heap_alloc(sizeof(nsProtocolStream));
218
219     ret->nsIInputStream_iface.lpVtbl = &nsInputStreamVtbl;
220     ret->ref = 1;
221     ret->buf_size = 0;
222
223     return ret;
224 }
225
226 static inline BSCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
227 {
228     return CONTAINING_RECORD(iface, BSCallback, IBindStatusCallback_iface);
229 }
230
231 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
232         REFIID riid, void **ppv)
233 {
234     BSCallback *This = impl_from_IBindStatusCallback(iface);
235
236     *ppv = NULL;
237     if(IsEqualGUID(&IID_IUnknown, riid)) {
238         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
239         *ppv = &This->IBindStatusCallback_iface;
240     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
241         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv);
242         *ppv = &This->IBindStatusCallback_iface;
243     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
244         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
245         *ppv = &This->IServiceProvider_iface;
246     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
247         TRACE("(%p)->(IID_IHttpNegotiate %p)\n", This, ppv);
248         *ppv = &This->IHttpNegotiate2_iface;
249     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
250         TRACE("(%p)->(IID_IHttpNegotiate2 %p)\n", This, ppv);
251         *ppv = &This->IHttpNegotiate2_iface;
252     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
253         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
254         *ppv = &This->IInternetBindInfo_iface;
255     }
256
257     if(*ppv) {
258         IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
259         return S_OK;
260     }
261
262     TRACE("Unsupported riid = %s\n", debugstr_guid(riid));
263     return E_NOINTERFACE;
264 }
265
266 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
267 {
268     BSCallback *This = impl_from_IBindStatusCallback(iface);
269     LONG ref = InterlockedIncrement(&This->ref);
270
271     TRACE("(%p) ref = %d\n", This, ref);
272
273     return ref;
274 }
275
276 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
277 {
278     BSCallback *This = impl_from_IBindStatusCallback(iface);
279     LONG ref = InterlockedDecrement(&This->ref);
280
281     TRACE("(%p) ref = %d\n", This, ref);
282
283     if(!ref) {
284         if(This->post_data)
285             GlobalFree(This->post_data);
286         if(This->mon)
287             IMoniker_Release(This->mon);
288         if(This->binding)
289             IBinding_Release(This->binding);
290         list_remove(&This->entry);
291         list_init(&This->entry);
292         heap_free(This->headers);
293
294         This->vtbl->destroy(This);
295     }
296
297     return ref;
298 }
299
300 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
301         DWORD dwReserved, IBinding *pbind)
302 {
303     BSCallback *This = impl_from_IBindStatusCallback(iface);
304
305     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);
306
307     IBinding_AddRef(pbind);
308     This->binding = pbind;
309
310     if(This->window)
311         list_add_head(&This->window->bindings, &This->entry);
312
313     return This->vtbl->start_binding(This);
314 }
315
316 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
317 {
318     BSCallback *This = impl_from_IBindStatusCallback(iface);
319     FIXME("(%p)->(%p)\n", This, pnPriority);
320     return E_NOTIMPL;
321 }
322
323 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
324 {
325     BSCallback *This = impl_from_IBindStatusCallback(iface);
326     FIXME("(%p)->(%d)\n", This, reserved);
327     return E_NOTIMPL;
328 }
329
330 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
331         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
332 {
333     BSCallback *This = impl_from_IBindStatusCallback(iface);
334
335     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
336             debugstr_w(szStatusText));
337
338     return This->vtbl->on_progress(This, ulStatusCode, szStatusText);
339 }
340
341 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
342         HRESULT hresult, LPCWSTR szError)
343 {
344     BSCallback *This = impl_from_IBindStatusCallback(iface);
345     HRESULT hres;
346
347     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
348
349     /* NOTE: IE7 calls GetBindResult here */
350
351     hres = This->vtbl->stop_binding(This, hresult);
352
353     if(This->binding) {
354         IBinding_Release(This->binding);
355         This->binding = NULL;
356     }
357
358     list_remove(&This->entry);
359     list_init(&This->entry);
360     This->window = NULL;
361
362     return hres;
363 }
364
365 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
366         DWORD *grfBINDF, BINDINFO *pbindinfo)
367 {
368     BSCallback *This = impl_from_IBindStatusCallback(iface);
369     DWORD size;
370
371     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
372
373     if(!This->bindinfo_ready) {
374         HRESULT hres;
375
376         hres = This->vtbl->init_bindinfo(This);
377         if(FAILED(hres))
378             return hres;
379
380         This->bindinfo_ready = TRUE;
381     }
382
383     *grfBINDF = This->bindf;
384
385     size = pbindinfo->cbSize;
386     memset(pbindinfo, 0, size);
387     pbindinfo->cbSize = size;
388
389     pbindinfo->cbstgmedData = This->post_data_len;
390     pbindinfo->dwCodePage = CP_UTF8;
391     pbindinfo->dwOptions = 0x80000;
392
393     if(This->post_data) {
394         pbindinfo->dwBindVerb = BINDVERB_POST;
395
396         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
397         pbindinfo->stgmedData.u.hGlobal = This->post_data;
398         pbindinfo->stgmedData.pUnkForRelease = (IUnknown*)&This->IBindStatusCallback_iface;
399         IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
400     }
401
402     return S_OK;
403 }
404
405 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
406         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
407 {
408     BSCallback *This = impl_from_IBindStatusCallback(iface);
409
410     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
411
412     return This->vtbl->read_data(This, pstgmed->u.pstm);
413 }
414
415 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
416         REFIID riid, IUnknown *punk)
417 {
418     BSCallback *This = impl_from_IBindStatusCallback(iface);
419     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);
420     return E_NOTIMPL;
421 }
422
423 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
424     BindStatusCallback_QueryInterface,
425     BindStatusCallback_AddRef,
426     BindStatusCallback_Release,
427     BindStatusCallback_OnStartBinding,
428     BindStatusCallback_GetPriority,
429     BindStatusCallback_OnLowResource,
430     BindStatusCallback_OnProgress,
431     BindStatusCallback_OnStopBinding,
432     BindStatusCallback_GetBindInfo,
433     BindStatusCallback_OnDataAvailable,
434     BindStatusCallback_OnObjectAvailable
435 };
436
437 static inline BSCallback *impl_from_IHttpNegotiate2(IHttpNegotiate2 *iface)
438 {
439     return CONTAINING_RECORD(iface, BSCallback, IHttpNegotiate2_iface);
440 }
441
442 static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
443                                                    REFIID riid, void **ppv)
444 {
445     BSCallback *This = impl_from_IHttpNegotiate2(iface);
446     return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv);
447 }
448
449 static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface)
450 {
451     BSCallback *This = impl_from_IHttpNegotiate2(iface);
452     return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
453 }
454
455 static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface)
456 {
457     BSCallback *This = impl_from_IHttpNegotiate2(iface);
458     return IBindStatusCallback_Release(&This->IBindStatusCallback_iface);
459 }
460
461 static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
462         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
463 {
464     BSCallback *This = impl_from_IHttpNegotiate2(iface);
465     HRESULT hres;
466
467     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders),
468           dwReserved, pszAdditionalHeaders);
469
470     *pszAdditionalHeaders = NULL;
471
472     hres = This->vtbl->beginning_transaction(This, pszAdditionalHeaders);
473     if(hres != S_FALSE)
474         return hres;
475
476     if(This->headers) {
477         DWORD size;
478
479         size = (strlenW(This->headers)+1)*sizeof(WCHAR);
480         *pszAdditionalHeaders = CoTaskMemAlloc(size);
481         if(!*pszAdditionalHeaders)
482             return E_OUTOFMEMORY;
483         memcpy(*pszAdditionalHeaders, This->headers, size);
484     }
485
486     return S_OK;
487 }
488
489 static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
490         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
491 {
492     BSCallback *This = impl_from_IHttpNegotiate2(iface);
493
494     TRACE("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
495           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
496
497     return This->vtbl->on_response(This, dwResponseCode, szResponseHeaders);
498 }
499
500 static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
501         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
502 {
503     BSCallback *This = impl_from_IHttpNegotiate2(iface);
504     FIXME("(%p)->(%p %p %ld)\n", This, pbSecurityId, pcbSecurityId, dwReserved);
505     return E_NOTIMPL;
506 }
507
508 static const IHttpNegotiate2Vtbl HttpNegotiate2Vtbl = {
509     HttpNegotiate_QueryInterface,
510     HttpNegotiate_AddRef,
511     HttpNegotiate_Release,
512     HttpNegotiate_BeginningTransaction,
513     HttpNegotiate_OnResponse,
514     HttpNegotiate_GetRootSecurityId
515 };
516
517 static inline BSCallback *impl_from_IInternetBindInfo(IInternetBindInfo *iface)
518 {
519     return CONTAINING_RECORD(iface, BSCallback, IInternetBindInfo_iface);
520 }
521
522 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
523                                                       REFIID riid, void **ppv)
524 {
525     BSCallback *This = impl_from_IInternetBindInfo(iface);
526     return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv);
527 }
528
529 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
530 {
531     BSCallback *This = impl_from_IInternetBindInfo(iface);
532     return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
533 }
534
535 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
536 {
537     BSCallback *This = impl_from_IInternetBindInfo(iface);
538     return IBindStatusCallback_Release(&This->IBindStatusCallback_iface);
539 }
540
541 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
542                                                    DWORD *grfBINDF, BINDINFO *pbindinfo)
543 {
544     BSCallback *This = impl_from_IInternetBindInfo(iface);
545     FIXME("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
546     return E_NOTIMPL;
547 }
548
549 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
550         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
551 {
552     BSCallback *This = impl_from_IInternetBindInfo(iface);
553     FIXME("(%p)->(%u %p %u %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
554     return E_NOTIMPL;
555 }
556
557 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
558     InternetBindInfo_QueryInterface,
559     InternetBindInfo_AddRef,
560     InternetBindInfo_Release,
561     InternetBindInfo_GetBindInfo,
562     InternetBindInfo_GetBindString
563 };
564
565 static inline BSCallback *impl_from_IServiceProvider(IServiceProvider *iface)
566 {
567     return CONTAINING_RECORD(iface, BSCallback, IServiceProvider_iface);
568 }
569
570 static HRESULT WINAPI BSCServiceProvider_QueryInterface(IServiceProvider *iface,
571                                                         REFIID riid, void **ppv)
572 {
573     BSCallback *This = impl_from_IServiceProvider(iface);
574     return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv);
575 }
576
577 static ULONG WINAPI BSCServiceProvider_AddRef(IServiceProvider *iface)
578 {
579     BSCallback *This = impl_from_IServiceProvider(iface);
580     return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
581 }
582
583 static ULONG WINAPI BSCServiceProvider_Release(IServiceProvider *iface)
584 {
585     BSCallback *This = impl_from_IServiceProvider(iface);
586     return IBindStatusCallback_Release(&This->IBindStatusCallback_iface);
587 }
588
589 static HRESULT WINAPI BSCServiceProvider_QueryService(IServiceProvider *iface,
590         REFGUID guidService, REFIID riid, void **ppv)
591 {
592     BSCallback *This = impl_from_IServiceProvider(iface);
593
594     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
595
596     if(This->window && IsEqualGUID(guidService, &IID_IWindowForBindingUI))
597         return IServiceProvider_QueryService(&This->window->base.IServiceProvider_iface, guidService, riid, ppv);
598     return E_NOINTERFACE;
599 }
600
601 static const IServiceProviderVtbl ServiceProviderVtbl = {
602     BSCServiceProvider_QueryInterface,
603     BSCServiceProvider_AddRef,
604     BSCServiceProvider_Release,
605     BSCServiceProvider_QueryService
606 };
607
608 static void init_bscallback(BSCallback *This, const BSCallbackVtbl *vtbl, IMoniker *mon, DWORD bindf)
609 {
610     This->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl;
611     This->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
612     This->IHttpNegotiate2_iface.lpVtbl = &HttpNegotiate2Vtbl;
613     This->IInternetBindInfo_iface.lpVtbl = &InternetBindInfoVtbl;
614     This->vtbl = vtbl;
615     This->ref = 1;
616     This->bindf = bindf;
617     This->bom = BOM_NONE;
618
619     list_init(&This->entry);
620
621     if(mon)
622         IMoniker_AddRef(mon);
623     This->mon = mon;
624 }
625
626 static HRESULT read_stream(BSCallback *This, IStream *stream, void *buf, DWORD size, DWORD *ret_size)
627 {
628     DWORD read_size = 0, skip=0;
629     BYTE *data = buf;
630     HRESULT hres;
631
632     hres = IStream_Read(stream, buf, size, &read_size);
633
634     if(!This->readed && This->bom == BOM_NONE) {
635         if(read_size >= 2 && data[0] == 0xff && data[1] == 0xfe) {
636             This->bom = BOM_UTF16;
637             skip = 2;
638         }else if(read_size >= 3 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf) {
639             This->bom = BOM_UTF8;
640             skip = 3;
641         }
642         if(skip) {
643             read_size -= skip;
644             if(read_size)
645                 memmove(data, data+skip, read_size);
646         }
647     }
648
649     This->readed += read_size;
650     *ret_size = read_size;
651     return hres;
652 }
653
654 static void parse_content_type(nsChannelBSC *This, const WCHAR *value)
655 {
656     const WCHAR *ptr;
657     size_t len;
658
659     static const WCHAR charsetW[] = {'c','h','a','r','s','e','t','='};
660
661     ptr = strchrW(value, ';');
662     if(!ptr)
663         return;
664
665     ptr++;
666     while(*ptr && isspaceW(*ptr))
667         ptr++;
668
669     len = strlenW(value);
670     if(ptr + sizeof(charsetW)/sizeof(WCHAR) < value+len && !memicmpW(ptr, charsetW, sizeof(charsetW)/sizeof(WCHAR))) {
671         size_t charset_len, lena;
672         nsACString charset_str;
673         const WCHAR *charset;
674         char *charseta;
675
676         ptr += sizeof(charsetW)/sizeof(WCHAR);
677
678         if(*ptr == '\'') {
679             FIXME("Quoted value\n");
680             return;
681         }else {
682             charset = ptr;
683             while(*ptr && *ptr != ',')
684                 ptr++;
685             charset_len = ptr-charset;
686         }
687
688         lena = WideCharToMultiByte(CP_ACP, 0, charset, charset_len, NULL, 0, NULL, NULL);
689         charseta = heap_alloc(lena+1);
690         if(!charseta)
691             return;
692
693         WideCharToMultiByte(CP_ACP, 0, charset, charset_len, charseta, lena, NULL, NULL);
694         charseta[lena] = 0;
695
696         nsACString_InitDepend(&charset_str, charseta);
697         nsIHttpChannel_SetContentCharset(&This->nschannel->nsIHttpChannel_iface, &charset_str);
698         nsACString_Finish(&charset_str);
699         heap_free(charseta);
700     }else {
701         FIXME("unhandled: %s\n", debugstr_wn(ptr, len - (ptr-value)));
702     }
703 }
704
705 static HRESULT parse_headers(const WCHAR *headers, struct list *headers_list)
706 {
707     const WCHAR *header, *header_end, *colon, *value;
708     HRESULT hres;
709
710     header = headers;
711     while(*header) {
712         if(header[0] == '\r' && header[1] == '\n' && !header[2])
713             break;
714         for(colon = header; *colon && *colon != ':' && *colon != '\r'; colon++);
715         if(*colon != ':')
716             return E_FAIL;
717
718         value = colon+1;
719         while(*value == ' ')
720             value++;
721         if(!*value)
722             return E_FAIL;
723
724         for(header_end = value+1; *header_end && *header_end != '\r'; header_end++);
725
726         hres = set_http_header(headers_list, header, colon-header, value, header_end-value);
727         if(FAILED(hres))
728             return hres;
729
730         header = header_end;
731         if(header[0] == '\r' && header[1] == '\n')
732             header += 2;
733     }
734
735     return S_OK;
736 }
737
738 static HRESULT process_response_headers(nsChannelBSC *This, const WCHAR *headers)
739 {
740     http_header_t *iter;
741     HRESULT hres;
742
743     static const WCHAR content_typeW[] = {'c','o','n','t','e','n','t','-','t','y','p','e',0};
744
745     hres = parse_headers(headers, &This->nschannel->response_headers);
746     if(FAILED(hres))
747         return hres;
748
749     LIST_FOR_EACH_ENTRY(iter, &This->nschannel->response_headers, http_header_t, entry) {
750         if(!strcmpiW(iter->header, content_typeW))
751             parse_content_type(This, iter->data);
752     }
753
754     return S_OK;
755 }
756
757 static void query_http_info(nsChannelBSC *This, IWinInetHttpInfo *wininet_info)
758 {
759     const WCHAR *ptr;
760     DWORD len = 0;
761     WCHAR *buf;
762
763     IWinInetHttpInfo_QueryInfo(wininet_info, HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &len, NULL, NULL);
764     if(!len)
765         return;
766
767     buf = heap_alloc(len);
768     if(!buf)
769         return;
770
771     IWinInetHttpInfo_QueryInfo(wininet_info, HTTP_QUERY_RAW_HEADERS_CRLF, buf, &len, NULL, NULL);
772     if(!len) {
773         heap_free(buf);
774         return;
775     }
776
777     ptr = strchrW(buf, '\r');
778     if(ptr && ptr[1] == '\n') {
779         ptr += 2;
780         process_response_headers(This, ptr);
781     }
782
783     heap_free(buf);
784 }
785
786 HRESULT start_binding(HTMLInnerWindow *inner_window, BSCallback *bscallback, IBindCtx *bctx)
787 {
788     IStream *str = NULL;
789     HRESULT hres;
790
791     TRACE("(%p %p %p)\n", inner_window, bscallback, bctx);
792
793     bscallback->window = inner_window;
794
795     /* NOTE: IE7 calls IsSystemMoniker here*/
796
797     if(bctx) {
798         RegisterBindStatusCallback(bctx, &bscallback->IBindStatusCallback_iface, NULL, 0);
799         IBindCtx_AddRef(bctx);
800     }else {
801         hres = CreateAsyncBindCtx(0, &bscallback->IBindStatusCallback_iface, NULL, &bctx);
802         if(FAILED(hres)) {
803             WARN("CreateAsyncBindCtx failed: %08x\n", hres);
804             bscallback->vtbl->stop_binding(bscallback, hres);
805             return hres;
806         }
807     }
808
809     hres = IMoniker_BindToStorage(bscallback->mon, bctx, NULL, &IID_IStream, (void**)&str);
810     IBindCtx_Release(bctx);
811     if(FAILED(hres)) {
812         WARN("BindToStorage failed: %08x\n", hres);
813         bscallback->vtbl->stop_binding(bscallback, hres);
814         return hres;
815     }
816
817     if(str)
818         IStream_Release(str);
819
820     IMoniker_Release(bscallback->mon);
821     bscallback->mon = NULL;
822
823     return S_OK;
824 }
825
826 typedef struct {
827     BSCallback bsc;
828
829     DWORD size;
830     char *buf;
831     HRESULT hres;
832 } BufferBSC;
833
834 static inline BufferBSC *BufferBSC_from_BSCallback(BSCallback *iface)
835 {
836     return CONTAINING_RECORD(iface, BufferBSC, bsc);
837 }
838
839 static void BufferBSC_destroy(BSCallback *bsc)
840 {
841     BufferBSC *This = BufferBSC_from_BSCallback(bsc);
842
843     heap_free(This->buf);
844     heap_free(This);
845 }
846
847 static HRESULT BufferBSC_init_bindinfo(BSCallback *bsc)
848 {
849     return S_OK;
850 }
851
852 static HRESULT BufferBSC_start_binding(BSCallback *bsc)
853 {
854     return S_OK;
855 }
856
857 static HRESULT BufferBSC_stop_binding(BSCallback *bsc, HRESULT result)
858 {
859     BufferBSC *This = BufferBSC_from_BSCallback(bsc);
860
861     This->hres = result;
862
863     if(FAILED(result)) {
864         heap_free(This->buf);
865         This->buf = NULL;
866         This->size = 0;
867     }
868
869     return S_OK;
870 }
871
872 static HRESULT BufferBSC_read_data(BSCallback *bsc, IStream *stream)
873 {
874     BufferBSC *This = BufferBSC_from_BSCallback(bsc);
875     DWORD readed;
876     HRESULT hres;
877
878     if(!This->buf) {
879         This->size = 128;
880         This->buf = heap_alloc(This->size);
881     }
882
883     do {
884         if(This->bsc.readed >= This->size) {
885             This->size <<= 1;
886             This->buf = heap_realloc(This->buf, This->size);
887         }
888
889         hres = read_stream(&This->bsc, stream, This->buf+This->bsc.readed, This->size-This->bsc.readed, &readed);
890     }while(hres == S_OK);
891
892     return S_OK;
893 }
894
895 static HRESULT BufferBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
896 {
897     return S_OK;
898 }
899
900 static HRESULT BufferBSC_on_response(BSCallback *bsc, DWORD response_code,
901         LPCWSTR response_headers)
902 {
903     return S_OK;
904 }
905
906 static HRESULT BufferBSC_beginning_transaction(BSCallback *bsc, WCHAR **additional_headers)
907 {
908     return S_FALSE;
909 }
910
911 static const BSCallbackVtbl BufferBSCVtbl = {
912     BufferBSC_destroy,
913     BufferBSC_init_bindinfo,
914     BufferBSC_start_binding,
915     BufferBSC_stop_binding,
916     BufferBSC_read_data,
917     BufferBSC_on_progress,
918     BufferBSC_on_response,
919     BufferBSC_beginning_transaction
920 };
921
922
923 static BufferBSC *create_bufferbsc(IMoniker *mon)
924 {
925     BufferBSC *ret = heap_alloc_zero(sizeof(*ret));
926
927     init_bscallback(&ret->bsc, &BufferBSCVtbl, mon, 0);
928     ret->hres = E_FAIL;
929
930     return ret;
931 }
932
933 HRESULT bind_mon_to_wstr(HTMLInnerWindow *window, IMoniker *mon, WCHAR **ret)
934 {
935     BufferBSC *bsc = create_bufferbsc(mon);
936     int cp = CP_ACP;
937     WCHAR *text;
938     HRESULT hres;
939
940     hres = start_binding(window, &bsc->bsc, NULL);
941     if(SUCCEEDED(hres))
942         hres = bsc->hres;
943     if(FAILED(hres)) {
944         IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
945         return hres;
946     }
947
948     if(!bsc->bsc.readed) {
949         *ret = NULL;
950         return S_OK;
951     }
952
953     switch(bsc->bsc.bom) {
954     case BOM_UTF16:
955         if(bsc->bsc.readed % sizeof(WCHAR)) {
956             FIXME("The buffer is not a valid utf16 string\n");
957             hres = E_FAIL;
958             break;
959         }
960
961         text = heap_alloc(bsc->bsc.readed+sizeof(WCHAR));
962         if(!text) {
963             hres = E_OUTOFMEMORY;
964             break;
965         }
966
967         memcpy(text, bsc->buf, bsc->bsc.readed);
968         text[bsc->bsc.readed/sizeof(WCHAR)] = 0;
969         break;
970
971     case BOM_UTF8:
972         cp = CP_UTF8;
973         /* fallthrough */
974     default: {
975         DWORD len;
976
977         len = MultiByteToWideChar(cp, 0, bsc->buf, bsc->bsc.readed, NULL, 0);
978         text = heap_alloc((len+1)*sizeof(WCHAR));
979         if(!text) {
980             hres = E_OUTOFMEMORY;
981             break;
982         }
983
984         MultiByteToWideChar(CP_ACP, 0, bsc->buf, bsc->bsc.readed, text, len);
985         text[len] = 0;
986     }
987     }
988
989     IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
990     if(FAILED(hres))
991         return hres;
992
993     *ret = text;
994     return S_OK;
995 }
996
997 static HRESULT read_post_data_stream(nsChannelBSC *This, nsChannel *nschannel)
998 {
999     PRUint32 data_len = 0, available = 0;
1000     char *data, *post_data;
1001     nsresult nsres;
1002     HRESULT hres = S_OK;
1003
1004     if(!nschannel->post_data_stream)
1005         return S_OK;
1006
1007     nsres =  nsIInputStream_Available(nschannel->post_data_stream, &available);
1008     if(NS_FAILED(nsres))
1009         return E_FAIL;
1010
1011     post_data = data = GlobalAlloc(0, available);
1012     if(!data)
1013         return E_OUTOFMEMORY;
1014
1015     nsres = nsIInputStream_Read(nschannel->post_data_stream, data, available, &data_len);
1016     if(NS_FAILED(nsres)) {
1017         GlobalFree(data);
1018         return E_FAIL;
1019     }
1020
1021     if(nschannel->post_data_contains_headers) {
1022         if(data_len >= 2 && data[0] == '\r' && data[1] == '\n') {
1023             post_data = data+2;
1024             data_len -= 2;
1025         }else {
1026             WCHAR *headers;
1027             DWORD size;
1028             char *ptr;
1029
1030             post_data += data_len;
1031             for(ptr = data; ptr+4 < data+data_len; ptr++) {
1032                 if(!memcmp(ptr, "\r\n\r\n", 4)) {
1033                     post_data = ptr+4;
1034                     break;
1035                 }
1036             }
1037
1038             data_len -= post_data-data;
1039
1040             size = MultiByteToWideChar(CP_ACP, 0, data, post_data-data, NULL, 0);
1041             headers = heap_alloc((size+1)*sizeof(WCHAR));
1042             if(headers) {
1043                 MultiByteToWideChar(CP_ACP, 0, data, post_data-data, headers, size);
1044                 headers[size] = 0;
1045                 hres = parse_headers(headers , &nschannel->request_headers);
1046                 if(SUCCEEDED(hres))
1047                     This->bsc.headers = headers;
1048                 else
1049                     heap_free(headers);
1050             }else {
1051                 hres = E_OUTOFMEMORY;
1052             }
1053         }
1054     }
1055
1056     if(FAILED(hres)) {
1057         GlobalFree(data);
1058         return hres;
1059     }
1060
1061     if(!data_len) {
1062         GlobalFree(data);
1063         post_data = NULL;
1064     }else if(post_data != data) {
1065         char *new_data;
1066
1067         new_data = GlobalAlloc(0, data_len);
1068         if(new_data)
1069             memcpy(new_data, post_data, data_len);
1070         GlobalFree(data);
1071         if(!new_data)
1072             return E_OUTOFMEMORY;
1073         post_data = new_data;
1074     }
1075
1076     This->bsc.post_data = post_data;
1077     This->bsc.post_data_len = data_len;
1078     TRACE("post_data = %s\n", debugstr_a(This->bsc.post_data));
1079     return S_OK;
1080 }
1081
1082 static HRESULT on_start_nsrequest(nsChannelBSC *This)
1083 {
1084     nsresult nsres;
1085
1086     /* FIXME: it's needed for http connections from BindToObject. */
1087     if(!This->nschannel->response_status)
1088         This->nschannel->response_status = 200;
1089
1090     nsres = nsIStreamListener_OnStartRequest(This->nslistener,
1091             (nsIRequest*)&This->nschannel->nsIHttpChannel_iface, This->nscontext);
1092     if(NS_FAILED(nsres)) {
1093         FIXME("OnStartRequest failed: %08x\n", nsres);
1094         return E_FAIL;
1095     }
1096
1097     if(This->is_doc_channel) {
1098         update_window_doc(This->bsc.window);
1099         if(This->bsc.window->base.outer_window->readystate != READYSTATE_LOADING)
1100             set_ready_state(This->bsc.window->base.outer_window, READYSTATE_LOADING);
1101     }
1102
1103     return S_OK;
1104 }
1105
1106 static void on_stop_nsrequest(nsChannelBSC *This, HRESULT result)
1107 {
1108     nsresult nsres, request_result;
1109
1110     switch(result) {
1111     case S_OK:
1112         request_result = NS_OK;
1113         break;
1114     case E_ABORT:
1115         request_result = NS_BINDING_ABORTED;
1116         break;
1117     default:
1118         request_result = NS_ERROR_FAILURE;
1119     }
1120
1121     if(This->nslistener) {
1122         nsres = nsIStreamListener_OnStopRequest(This->nslistener,
1123                  (nsIRequest*)&This->nschannel->nsIHttpChannel_iface, This->nscontext,
1124                  request_result);
1125         if(NS_FAILED(nsres))
1126             WARN("OnStopRequest failed: %08x\n", nsres);
1127     }
1128
1129     if(This->nschannel->load_group) {
1130         nsres = nsILoadGroup_RemoveRequest(This->nschannel->load_group,
1131                 (nsIRequest*)&This->nschannel->nsIHttpChannel_iface, NULL, request_result);
1132         if(NS_FAILED(nsres))
1133             ERR("RemoveRequest failed: %08x\n", nsres);
1134     }
1135 }
1136
1137 static HRESULT read_stream_data(nsChannelBSC *This, IStream *stream)
1138 {
1139     DWORD read;
1140     nsresult nsres;
1141     HRESULT hres;
1142
1143     if(!This->response_processed) {
1144         IWinInetHttpInfo *wininet_info;
1145
1146         This->response_processed = TRUE;
1147         if(This->bsc.binding) {
1148             hres = IBinding_QueryInterface(This->bsc.binding, &IID_IWinInetHttpInfo, (void**)&wininet_info);
1149             if(SUCCEEDED(hres)) {
1150                 query_http_info(This, wininet_info);
1151                 IWinInetHttpInfo_Release(wininet_info);
1152             }
1153         }
1154     }
1155
1156     if(!This->nslistener) {
1157         BYTE buf[1024];
1158
1159         do {
1160             hres = read_stream(&This->bsc, stream, buf, sizeof(buf), &read);
1161         }while(hres == S_OK && read);
1162
1163         return S_OK;
1164     }
1165
1166     if(!This->nsstream)
1167         This->nsstream = create_nsprotocol_stream();
1168
1169     do {
1170         BOOL first_read = !This->bsc.readed;
1171
1172         hres = read_stream(&This->bsc, stream, This->nsstream->buf+This->nsstream->buf_size,
1173                 sizeof(This->nsstream->buf)-This->nsstream->buf_size, &read);
1174         if(!read)
1175             break;
1176
1177         This->nsstream->buf_size += read;
1178
1179         if(first_read) {
1180             switch(This->bsc.bom) {
1181             case BOM_UTF8:
1182                 This->nschannel->charset = heap_strdupA(UTF8_STR);
1183                 break;
1184             case BOM_UTF16:
1185                 This->nschannel->charset = heap_strdupA(UTF16_STR);
1186             }
1187
1188             if(!This->nschannel->content_type) {
1189                 WCHAR *mime;
1190
1191                 hres = FindMimeFromData(NULL, NULL, This->nsstream->buf, This->nsstream->buf_size,
1192                         This->is_doc_channel ? text_htmlW : NULL, 0, &mime, 0);
1193                 if(FAILED(hres))
1194                     return hres;
1195
1196                 TRACE("Found MIME %s\n", debugstr_w(mime));
1197
1198                 This->nschannel->content_type = heap_strdupWtoA(mime);
1199                 CoTaskMemFree(mime);
1200                 if(!This->nschannel->content_type)
1201                     return E_OUTOFMEMORY;
1202             }
1203
1204             on_start_nsrequest(This);
1205         }
1206
1207         nsres = nsIStreamListener_OnDataAvailable(This->nslistener,
1208                 (nsIRequest*)&This->nschannel->nsIHttpChannel_iface, This->nscontext,
1209                 &This->nsstream->nsIInputStream_iface, This->bsc.readed-This->nsstream->buf_size,
1210                 This->nsstream->buf_size);
1211         if(NS_FAILED(nsres))
1212             ERR("OnDataAvailable failed: %08x\n", nsres);
1213
1214         if(This->nsstream->buf_size == sizeof(This->nsstream->buf)) {
1215             ERR("buffer is full\n");
1216             break;
1217         }
1218     }while(hres == S_OK);
1219
1220     return S_OK;
1221 }
1222
1223 typedef struct {
1224     nsIAsyncVerifyRedirectCallback nsIAsyncVerifyRedirectCallback_iface;
1225
1226     LONG ref;
1227
1228     nsChannel *nschannel;
1229     nsChannelBSC *bsc;
1230 } nsRedirectCallback;
1231
1232 static nsRedirectCallback *impl_from_nsIAsyncVerifyRedirectCallback(nsIAsyncVerifyRedirectCallback *iface)
1233 {
1234     return CONTAINING_RECORD(iface, nsRedirectCallback, nsIAsyncVerifyRedirectCallback_iface);
1235 }
1236
1237 static nsresult NSAPI nsAsyncVerifyRedirectCallback_QueryInterface(nsIAsyncVerifyRedirectCallback *iface,
1238         nsIIDRef riid, void **result)
1239 {
1240     nsRedirectCallback *This = impl_from_nsIAsyncVerifyRedirectCallback(iface);
1241
1242     if(IsEqualGUID(&IID_nsISupports, riid)) {
1243         TRACE("(%p)->(IID_nsISuports %p)\n", This, result);
1244         *result = &This->nsIAsyncVerifyRedirectCallback_iface;
1245     }else if(IsEqualGUID(&IID_nsIAsyncVerifyRedirectCallback, riid)) {
1246         TRACE("(%p)->(IID_nsIAsyncVerifyRedirectCallback %p)\n", This, result);
1247         *result = &This->nsIAsyncVerifyRedirectCallback_iface;
1248     }else {
1249         *result = NULL;
1250         WARN("unimplemented iface %s\n", debugstr_guid(riid));
1251         return NS_NOINTERFACE;
1252     }
1253
1254     nsISupports_AddRef((nsISupports*)*result);
1255     return NS_OK;
1256 }
1257
1258 static nsrefcnt NSAPI nsAsyncVerifyRedirectCallback_AddRef(nsIAsyncVerifyRedirectCallback *iface)
1259 {
1260     nsRedirectCallback *This = impl_from_nsIAsyncVerifyRedirectCallback(iface);
1261     LONG ref = InterlockedIncrement(&This->ref);
1262
1263     TRACE("(%p) ref=%d\n", This, ref);
1264
1265     return ref;
1266 }
1267
1268 static nsrefcnt NSAPI nsAsyncVerifyRedirectCallback_Release(nsIAsyncVerifyRedirectCallback *iface)
1269 {
1270     nsRedirectCallback *This = impl_from_nsIAsyncVerifyRedirectCallback(iface);
1271     LONG ref = InterlockedDecrement(&This->ref);
1272
1273     TRACE("(%p) ref=%d\n", This, ref);
1274
1275     if(!ref) {
1276         IBindStatusCallback_Release(&This->bsc->bsc.IBindStatusCallback_iface);
1277         nsIHttpChannel_Release(&This->nschannel->nsIHttpChannel_iface);
1278         heap_free(This);
1279     }
1280
1281     return ref;
1282 }
1283
1284 static nsresult NSAPI nsAsyncVerifyRedirectCallback_AsyncOnChannelRedirect(nsIAsyncVerifyRedirectCallback *iface, nsresult result)
1285 {
1286     nsRedirectCallback *This = impl_from_nsIAsyncVerifyRedirectCallback(iface);
1287     nsChannel *old_nschannel;
1288     nsresult nsres;
1289
1290     TRACE("(%p)->(%08x)\n", This, result);
1291
1292     old_nschannel = This->bsc->nschannel;
1293     nsIHttpChannel_AddRef(&This->nschannel->nsIHttpChannel_iface);
1294     This->bsc->nschannel = This->nschannel;
1295
1296     if(This->nschannel->load_group) {
1297         nsres = nsILoadGroup_AddRequest(This->nschannel->load_group, (nsIRequest*)&This->nschannel->nsIHttpChannel_iface,
1298                 NULL);
1299         if(NS_FAILED(nsres))
1300             ERR("AddRequest failed: %08x\n", nsres);
1301     }
1302
1303     if(This->bsc->is_doc_channel) {
1304         IUri *uri = nsuri_get_uri(This->nschannel->uri);
1305
1306         if(uri) {
1307             set_current_uri(This->bsc->bsc.window->base.outer_window, uri);
1308             IUri_Release(uri);
1309         }else {
1310             WARN("Could not get IUri from nsWineURI\n");
1311         }
1312     }
1313
1314     if(old_nschannel) {
1315         if(old_nschannel->load_group) {
1316             nsres = nsILoadGroup_RemoveRequest(old_nschannel->load_group,
1317                     (nsIRequest*)&old_nschannel->nsIHttpChannel_iface, NULL, NS_OK);
1318             if(NS_FAILED(nsres))
1319                 ERR("RemoveRequest failed: %08x\n", nsres);
1320         }
1321         nsIHttpChannel_Release(&old_nschannel->nsIHttpChannel_iface);
1322     }
1323
1324     return NS_OK;
1325 }
1326
1327 static const nsIAsyncVerifyRedirectCallbackVtbl nsAsyncVerifyRedirectCallbackVtbl = {
1328     nsAsyncVerifyRedirectCallback_QueryInterface,
1329     nsAsyncVerifyRedirectCallback_AddRef,
1330     nsAsyncVerifyRedirectCallback_Release,
1331     nsAsyncVerifyRedirectCallback_AsyncOnChannelRedirect
1332 };
1333
1334 static HRESULT create_redirect_callback(nsChannel *nschannel, nsChannelBSC *bsc, nsRedirectCallback **ret)
1335 {
1336     nsRedirectCallback *callback;
1337
1338     callback = heap_alloc(sizeof(*callback));
1339     if(!callback)
1340         return E_OUTOFMEMORY;
1341
1342     callback->nsIAsyncVerifyRedirectCallback_iface.lpVtbl = &nsAsyncVerifyRedirectCallbackVtbl;
1343     callback->ref = 1;
1344
1345     nsIHttpChannel_AddRef(&nschannel->nsIHttpChannel_iface);
1346     callback->nschannel = nschannel;
1347
1348     IBindStatusCallback_AddRef(&bsc->bsc.IBindStatusCallback_iface);
1349     callback->bsc = bsc;
1350
1351     *ret = callback;
1352     return S_OK;
1353 }
1354
1355 static inline nsChannelBSC *nsChannelBSC_from_BSCallback(BSCallback *iface)
1356 {
1357     return CONTAINING_RECORD(iface, nsChannelBSC, bsc);
1358 }
1359
1360 static void nsChannelBSC_destroy(BSCallback *bsc)
1361 {
1362     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1363
1364     if(This->nschannel)
1365         nsIHttpChannel_Release(&This->nschannel->nsIHttpChannel_iface);
1366     if(This->nslistener)
1367         nsIStreamListener_Release(This->nslistener);
1368     if(This->nscontext)
1369         nsISupports_Release(This->nscontext);
1370     if(This->nsstream)
1371         nsIInputStream_Release(&This->nsstream->nsIInputStream_iface);
1372     heap_free(This);
1373 }
1374
1375 static HRESULT nsChannelBSC_start_binding(BSCallback *bsc)
1376 {
1377     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1378
1379     if(This->is_doc_channel)
1380         This->bsc.window->base.outer_window->base.inner_window->doc->skip_mutation_notif = FALSE;
1381
1382     return S_OK;
1383 }
1384
1385 static HRESULT nsChannelBSC_init_bindinfo(BSCallback *bsc)
1386 {
1387     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1388     HRESULT hres;
1389
1390     if(This->nschannel && This->nschannel->post_data_stream) {
1391         hres = read_post_data_stream(This, This->nschannel);
1392         if(FAILED(hres))
1393             return hres;
1394     }
1395
1396     return S_OK;
1397 }
1398
1399 typedef struct {
1400     task_t header;
1401     nsChannelBSC *bsc;
1402 } stop_request_task_t;
1403
1404 static void stop_request_proc(task_t *_task)
1405 {
1406     stop_request_task_t *task = (stop_request_task_t*)_task;
1407
1408     TRACE("(%p)\n", task->bsc);
1409
1410     list_remove(&task->bsc->bsc.entry);
1411     list_init(&task->bsc->bsc.entry);
1412     on_stop_nsrequest(task->bsc, S_OK);
1413 }
1414
1415 static void stop_request_task_destr(task_t *_task)
1416 {
1417     stop_request_task_t *task = (stop_request_task_t*)_task;
1418
1419     IBindStatusCallback_Release(&task->bsc->bsc.IBindStatusCallback_iface);
1420     heap_free(task);
1421 }
1422
1423 static HRESULT async_stop_request(nsChannelBSC *This)
1424 {
1425     stop_request_task_t *task;
1426
1427     if(!This->bsc.readed) {
1428         TRACE("No data read, calling OnStartRequest\n");
1429         on_start_nsrequest(This);
1430     }
1431
1432     task = heap_alloc(sizeof(*task));
1433     if(!task)
1434         return E_OUTOFMEMORY;
1435
1436     IBindStatusCallback_AddRef(&This->bsc.IBindStatusCallback_iface);
1437     task->bsc = This;
1438
1439     push_task(&task->header, stop_request_proc, stop_request_task_destr, This->bsc.window->task_magic);
1440     return S_OK;
1441 }
1442
1443 static void handle_navigation_error(nsChannelBSC *This, DWORD result)
1444 {
1445     HTMLOuterWindow *outer_window;
1446     HTMLDocumentObj *doc;
1447     IOleCommandTarget *olecmd;
1448     BOOL is_error_url;
1449     SAFEARRAY *sa;
1450     SAFEARRAYBOUND bound;
1451     VARIANT var, varOut;
1452     LONG ind;
1453     BSTR unk;
1454     HRESULT hres;
1455
1456     if(!This->is_doc_channel || !This->bsc.window)
1457         return;
1458
1459     outer_window = This->bsc.window->base.outer_window;
1460
1461     doc = outer_window->doc_obj;
1462     if(!doc || !doc->doc_object_service || !doc->client)
1463         return;
1464
1465     hres = IDocObjectService_IsErrorUrl(doc->doc_object_service,
1466             outer_window->url, &is_error_url);
1467     if(FAILED(hres) || is_error_url)
1468         return;
1469
1470     hres = IOleClientSite_QueryInterface(doc->client,
1471             &IID_IOleCommandTarget, (void**)&olecmd);
1472     if(FAILED(hres))
1473         return;
1474
1475     bound.lLbound = 0;
1476     bound.cElements = 8;
1477     sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
1478     if(!sa) {
1479         IOleCommandTarget_Release(olecmd);
1480         return;
1481     }
1482
1483     ind = 0;
1484     V_VT(&var) = VT_I4;
1485     V_I4(&var) = result;
1486     SafeArrayPutElement(sa, &ind, &var);
1487
1488     ind = 1;
1489     V_VT(&var) = VT_BSTR;
1490     V_BSTR(&var) = outer_window->url;
1491     SafeArrayPutElement(sa, &ind, &var);
1492
1493     ind = 3;
1494     V_VT(&var) = VT_UNKNOWN;
1495     V_UNKNOWN(&var) = (IUnknown*)&outer_window->base.IHTMLWindow2_iface;
1496     SafeArrayPutElement(sa, &ind, &var);
1497
1498     /* FIXME: what are the following fields for? */
1499     ind = 2;
1500     V_VT(&var) = VT_UNKNOWN;
1501     V_UNKNOWN(&var) = NULL;
1502     SafeArrayPutElement(sa, &ind, &var);
1503
1504     ind = 4;
1505     V_VT(&var) = VT_BOOL;
1506     V_BOOL(&var) = FALSE;
1507     SafeArrayPutElement(sa, &ind, &var);
1508
1509     ind = 5;
1510     V_VT(&var) = VT_BOOL;
1511     V_BOOL(&var) = FALSE;
1512     SafeArrayPutElement(sa, &ind, &var);
1513
1514     ind = 6;
1515     V_VT(&var) = VT_BSTR;
1516     unk = SysAllocString(NULL);
1517     V_BSTR(&var) = unk;
1518     SafeArrayPutElement(sa, &ind, &var);
1519
1520     ind = 7;
1521     V_VT(&var) = VT_UNKNOWN;
1522     V_UNKNOWN(&var) = NULL;
1523     SafeArrayPutElement(sa, &ind, &var);
1524
1525     V_VT(&var) = VT_ARRAY;
1526     V_ARRAY(&var) = sa;
1527     V_VT(&varOut) = VT_BOOL;
1528     V_BOOL(&varOut) = VARIANT_TRUE;
1529     IOleCommandTarget_Exec(olecmd, &CGID_DocHostCmdPriv, 1, 0, &var, FAILED(hres)?NULL:&varOut);
1530
1531     SysFreeString(unk);
1532     SafeArrayDestroy(sa);
1533     IOleCommandTarget_Release(olecmd);
1534 }
1535
1536 static HRESULT nsChannelBSC_stop_binding(BSCallback *bsc, HRESULT result)
1537 {
1538     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1539
1540     if(result != E_ABORT) {
1541         if(FAILED(result))
1542             handle_navigation_error(This, result);
1543         else if(This->is_doc_channel) {
1544             result = async_stop_request(This);
1545             if(SUCCEEDED(result))
1546                 return S_OK;
1547         }
1548     }
1549
1550     on_stop_nsrequest(This, result);
1551     return S_OK;
1552 }
1553
1554 static HRESULT nsChannelBSC_read_data(BSCallback *bsc, IStream *stream)
1555 {
1556     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1557
1558     return read_stream_data(This, stream);
1559 }
1560
1561 static HRESULT handle_redirect(nsChannelBSC *This, const WCHAR *new_url)
1562 {
1563     nsRedirectCallback *callback;
1564     nsIChannelEventSink *sink;
1565     nsChannel *new_channel;
1566     nsresult nsres;
1567     HRESULT hres;
1568
1569     TRACE("(%p)->(%s)\n", This, debugstr_w(new_url));
1570
1571     if(!This->nschannel || !This->nschannel->notif_callback)
1572         return S_OK;
1573
1574     nsres = nsIInterfaceRequestor_GetInterface(This->nschannel->notif_callback, &IID_nsIChannelEventSink, (void**)&sink);
1575     if(NS_FAILED(nsres))
1576         return S_OK;
1577
1578     hres = create_redirect_nschannel(new_url, This->nschannel, &new_channel);
1579     if(SUCCEEDED(hres)) {
1580         TRACE("%p %p->%p\n", This, This->nschannel, new_channel);
1581
1582         hres = create_redirect_callback(new_channel, This, &callback);
1583         nsIHttpChannel_Release(&new_channel->nsIHttpChannel_iface);
1584     }
1585
1586     if(SUCCEEDED(hres)) {
1587         nsres = nsIChannelEventSink_AsyncOnChannelRedirect(sink, (nsIChannel*)&This->nschannel->nsIHttpChannel_iface,
1588                 (nsIChannel*)&callback->nschannel->nsIHttpChannel_iface, REDIRECT_TEMPORARY, /* FIXME */
1589                 &callback->nsIAsyncVerifyRedirectCallback_iface);
1590
1591         if(NS_FAILED(nsres))
1592             FIXME("AsyncOnChannelRedirect failed: %08x\n", hres);
1593         else if(This->nschannel != callback->nschannel)
1594             FIXME("nschannel not updated\n");
1595
1596         nsIAsyncVerifyRedirectCallback_Release(&callback->nsIAsyncVerifyRedirectCallback_iface);
1597     }
1598
1599     nsIChannelEventSink_Release(sink);
1600     return hres;
1601 }
1602
1603 static HRESULT nsChannelBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
1604 {
1605     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1606
1607     switch(status_code) {
1608     case BINDSTATUS_MIMETYPEAVAILABLE:
1609         if(!This->nschannel)
1610             return S_OK;
1611
1612         heap_free(This->nschannel->content_type);
1613         This->nschannel->content_type = heap_strdupWtoA(status_text);
1614         break;
1615     case BINDSTATUS_REDIRECTING:
1616         return handle_redirect(This, status_text);
1617     case BINDSTATUS_BEGINDOWNLOADDATA: {
1618         IWinInetHttpInfo *http_info;
1619         DWORD status, size = sizeof(DWORD);
1620         HRESULT hres;
1621
1622         if(!This->bsc.binding)
1623             break;
1624
1625         hres = IBinding_QueryInterface(This->bsc.binding, &IID_IWinInetHttpInfo, (void**)&http_info);
1626         if(FAILED(hres))
1627             break;
1628
1629         hres = IWinInetHttpInfo_QueryInfo(http_info,
1630                 HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &status, &size, NULL, NULL);
1631         IWinInetHttpInfo_Release(http_info);
1632         if(FAILED(hres) || status == HTTP_STATUS_OK)
1633             break;
1634
1635         handle_navigation_error(This, status);
1636     }
1637     }
1638
1639     return S_OK;
1640 }
1641
1642 static HRESULT nsChannelBSC_on_response(BSCallback *bsc, DWORD response_code,
1643         LPCWSTR response_headers)
1644 {
1645     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1646     HRESULT hres;
1647
1648     This->response_processed = TRUE;
1649     This->nschannel->response_status = response_code;
1650
1651     if(response_headers) {
1652         const WCHAR *headers;
1653
1654         headers = strchrW(response_headers, '\r');
1655         if(headers && headers[1] == '\n') {
1656             headers += 2;
1657             hres = process_response_headers(This, headers);
1658             if(FAILED(hres)) {
1659                 WARN("parsing headers failed: %08x\n", hres);
1660                 return hres;
1661             }
1662         }
1663     }
1664
1665     return S_OK;
1666 }
1667
1668 static HRESULT nsChannelBSC_beginning_transaction(BSCallback *bsc, WCHAR **additional_headers)
1669 {
1670     nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
1671     http_header_t *iter;
1672     DWORD len = 0;
1673     WCHAR *ptr;
1674
1675     static const WCHAR content_lengthW[] =
1676         {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
1677
1678     if(!This->nschannel)
1679         return S_FALSE;
1680
1681     LIST_FOR_EACH_ENTRY(iter, &This->nschannel->request_headers, http_header_t, entry) {
1682         if(strcmpW(iter->header, content_lengthW))
1683             len += strlenW(iter->header) + 2 /* ": " */ + strlenW(iter->data) + 2 /* "\r\n" */;
1684     }
1685
1686     if(!len)
1687         return S_OK;
1688
1689     *additional_headers = ptr = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
1690     if(!ptr)
1691         return E_OUTOFMEMORY;
1692
1693     LIST_FOR_EACH_ENTRY(iter, &This->nschannel->request_headers, http_header_t, entry) {
1694         if(!strcmpW(iter->header, content_lengthW))
1695             continue;
1696
1697         len = strlenW(iter->header);
1698         memcpy(ptr, iter->header, len*sizeof(WCHAR));
1699         ptr += len;
1700
1701         *ptr++ = ':';
1702         *ptr++ = ' ';
1703
1704         len = strlenW(iter->data);
1705         memcpy(ptr, iter->data, len*sizeof(WCHAR));
1706         ptr += len;
1707
1708         *ptr++ = '\r';
1709         *ptr++ = '\n';
1710     }
1711
1712     *ptr = 0;
1713
1714     return S_OK;
1715 }
1716
1717 static const BSCallbackVtbl nsChannelBSCVtbl = {
1718     nsChannelBSC_destroy,
1719     nsChannelBSC_init_bindinfo,
1720     nsChannelBSC_start_binding,
1721     nsChannelBSC_stop_binding,
1722     nsChannelBSC_read_data,
1723     nsChannelBSC_on_progress,
1724     nsChannelBSC_on_response,
1725     nsChannelBSC_beginning_transaction
1726 };
1727
1728 HRESULT create_channelbsc(IMoniker *mon, const WCHAR *headers, BYTE *post_data, DWORD post_data_size,
1729         BOOL is_doc_binding, nsChannelBSC **retval)
1730 {
1731     nsChannelBSC *ret;
1732
1733     ret = heap_alloc_zero(sizeof(*ret));
1734     if(!ret)
1735         return E_OUTOFMEMORY;
1736
1737     init_bscallback(&ret->bsc, &nsChannelBSCVtbl, mon, BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA);
1738     ret->is_doc_channel = is_doc_binding;
1739
1740     if(headers) {
1741         ret->bsc.headers = heap_strdupW(headers);
1742         if(!ret->bsc.headers) {
1743             IBindStatusCallback_Release(&ret->bsc.IBindStatusCallback_iface);
1744             return E_OUTOFMEMORY;
1745         }
1746     }
1747
1748     if(post_data) {
1749         ret->bsc.post_data = GlobalAlloc(0, post_data_size);
1750         if(!ret->bsc.post_data) {
1751             heap_free(ret->bsc.headers);
1752             IBindStatusCallback_Release(&ret->bsc.IBindStatusCallback_iface);
1753             return E_OUTOFMEMORY;
1754         }
1755
1756         memcpy(ret->bsc.post_data, post_data, post_data_size);
1757         ret->bsc.post_data_len = post_data_size;
1758     }
1759
1760     *retval = ret;
1761     return S_OK;
1762 }
1763
1764 typedef struct {
1765     task_t header;
1766     HTMLOuterWindow *window;
1767     HTMLInnerWindow *pending_window;
1768 } start_doc_binding_task_t;
1769
1770 static void start_doc_binding_proc(task_t *_task)
1771 {
1772     start_doc_binding_task_t *task = (start_doc_binding_task_t*)_task;
1773
1774     set_current_mon(task->window, task->pending_window->bscallback->bsc.mon);
1775     start_binding(task->pending_window, &task->pending_window->bscallback->bsc, NULL);
1776 }
1777
1778 static void start_doc_binding_task_destr(task_t *_task)
1779 {
1780     start_doc_binding_task_t *task = (start_doc_binding_task_t*)_task;
1781
1782     IHTMLWindow2_Release(&task->pending_window->base.IHTMLWindow2_iface);
1783     heap_free(task);
1784 }
1785
1786 HRESULT async_start_doc_binding(HTMLOuterWindow *window, HTMLInnerWindow *pending_window)
1787 {
1788     start_doc_binding_task_t *task;
1789
1790     TRACE("%p\n", pending_window);
1791
1792     task = heap_alloc(sizeof(start_doc_binding_task_t));
1793     if(!task)
1794         return E_OUTOFMEMORY;
1795
1796     task->window = window;
1797     task->pending_window = pending_window;
1798     IHTMLWindow2_AddRef(&pending_window->base.IHTMLWindow2_iface);
1799
1800     push_task(&task->header, start_doc_binding_proc, start_doc_binding_task_destr, pending_window->task_magic);
1801     return S_OK;
1802 }
1803
1804 void abort_window_bindings(HTMLInnerWindow *window)
1805 {
1806     BSCallback *iter;
1807
1808     remove_target_tasks(window->task_magic);
1809
1810     while(!list_empty(&window->bindings)) {
1811         iter = LIST_ENTRY(window->bindings.next, BSCallback, entry);
1812
1813         TRACE("Aborting %p\n", iter);
1814
1815         IBindStatusCallback_AddRef(&iter->IBindStatusCallback_iface);
1816
1817         if(iter->binding)
1818             IBinding_Abort(iter->binding);
1819         else
1820             iter->vtbl->stop_binding(iter, E_ABORT);
1821
1822         iter->window = NULL;
1823         list_remove(&iter->entry);
1824         list_init(&iter->entry);
1825
1826         IBindStatusCallback_Release(&iter->IBindStatusCallback_iface);
1827     }
1828
1829     if(window->bscallback) {
1830         IBindStatusCallback_Release(&window->bscallback->bsc.IBindStatusCallback_iface);
1831         window->bscallback = NULL;
1832     }
1833
1834     if(window->mon) {
1835         IMoniker_Release(window->mon);
1836         window->mon = NULL;
1837     }
1838 }
1839
1840 HRESULT channelbsc_load_stream(HTMLInnerWindow *pending_window, IStream *stream)
1841 {
1842     nsChannelBSC *bscallback = pending_window->bscallback;
1843     HRESULT hres = S_OK;
1844
1845     if(!bscallback->nschannel) {
1846         ERR("NULL nschannel\n");
1847         return E_FAIL;
1848     }
1849
1850     bscallback->nschannel->content_type = heap_strdupA("text/html");
1851     if(!bscallback->nschannel->content_type)
1852         return E_OUTOFMEMORY;
1853
1854     bscallback->bsc.window = pending_window;
1855     if(stream)
1856         hres = read_stream_data(bscallback, stream);
1857     if(SUCCEEDED(hres))
1858         hres = async_stop_request(bscallback);
1859     if(FAILED(hres))
1860         IBindStatusCallback_OnStopBinding(&bscallback->bsc.IBindStatusCallback_iface, hres,
1861                 ERROR_SUCCESS);
1862
1863     return hres;
1864 }
1865
1866 void channelbsc_set_channel(nsChannelBSC *This, nsChannel *channel, nsIStreamListener *listener, nsISupports *context)
1867 {
1868     nsIHttpChannel_AddRef(&channel->nsIHttpChannel_iface);
1869     This->nschannel = channel;
1870
1871     nsIStreamListener_AddRef(listener);
1872     This->nslistener = listener;
1873
1874     if(context) {
1875         nsISupports_AddRef(context);
1876         This->nscontext = context;
1877     }
1878
1879     if(This->bsc.headers) {
1880         HRESULT hres;
1881
1882         hres = parse_headers(This->bsc.headers, &channel->request_headers);
1883         heap_free(This->bsc.headers);
1884         This->bsc.headers = NULL;
1885         if(FAILED(hres))
1886             WARN("parse_headers failed: %08x\n", hres);
1887     }
1888 }
1889
1890 typedef struct {
1891     task_t header;
1892     HTMLOuterWindow *window;
1893     IUri *uri;
1894 } navigate_javascript_task_t;
1895
1896 static void navigate_javascript_proc(task_t *_task)
1897 {
1898     navigate_javascript_task_t *task = (navigate_javascript_task_t*)_task;
1899     HTMLOuterWindow *window = task->window;
1900     VARIANT v;
1901     BSTR code;
1902     HRESULT hres;
1903
1904     static const WCHAR jscriptW[] = {'j','s','c','r','i','p','t',0};
1905
1906     task->window->readystate = READYSTATE_COMPLETE;
1907
1908     hres = IUri_GetPath(task->uri, &code);
1909     if(FAILED(hres))
1910         return;
1911
1912     hres = UrlUnescapeW(code, NULL, NULL, URL_UNESCAPE_INPLACE);
1913     if(FAILED(hres)) {
1914         SysFreeString(code);
1915         return;
1916     }
1917
1918     set_download_state(window->doc_obj, 1);
1919
1920     V_VT(&v) = VT_EMPTY;
1921     hres = exec_script(window->base.inner_window, code, jscriptW, &v);
1922     SysFreeString(code);
1923     if(SUCCEEDED(hres) && V_VT(&v) != VT_EMPTY) {
1924         FIXME("javascirpt URL returned %s\n", debugstr_variant(&v));
1925         VariantClear(&v);
1926     }
1927
1928     if(window->doc_obj->view_sink)
1929         IAdviseSink_OnViewChange(window->doc_obj->view_sink, DVASPECT_CONTENT, -1);
1930
1931     set_download_state(window->doc_obj, 0);
1932 }
1933
1934 static void navigate_javascript_task_destr(task_t *_task)
1935 {
1936     navigate_javascript_task_t *task = (navigate_javascript_task_t*)_task;
1937
1938     IUri_Release(task->uri);
1939     heap_free(task);
1940 }
1941
1942 typedef struct {
1943     task_t header;
1944     HTMLOuterWindow *window;
1945     nsChannelBSC *bscallback;
1946     IMoniker *mon;
1947 } navigate_task_t;
1948
1949 static void navigate_proc(task_t *_task)
1950 {
1951     navigate_task_t *task = (navigate_task_t*)_task;
1952     HRESULT hres;
1953
1954     hres = set_moniker(&task->window->doc_obj->basedoc, task->mon, NULL, task->bscallback, TRUE);
1955     if(SUCCEEDED(hres)) {
1956         set_current_mon(task->window, task->bscallback->bsc.mon);
1957         start_binding(task->window->pending_window, &task->bscallback->bsc, NULL);
1958     }
1959 }
1960
1961 static void navigate_task_destr(task_t *_task)
1962 {
1963     navigate_task_t *task = (navigate_task_t*)_task;
1964
1965     IBindStatusCallback_Release(&task->bscallback->bsc.IBindStatusCallback_iface);
1966     IMoniker_Release(task->mon);
1967     heap_free(task);
1968 }
1969
1970 static HRESULT navigate_fragment(HTMLOuterWindow *window, IUri *uri)
1971 {
1972     nsIDOMLocation *nslocation;
1973     nsAString nsfrag_str;
1974     WCHAR *selector;
1975     BSTR frag;
1976     nsresult nsres;
1977     HRESULT hres;
1978
1979     const WCHAR selector_formatW[] = {'a','[','i','d','=','"','%','s','"',']',0};
1980
1981     set_current_uri(window, uri);
1982
1983     nsres = nsIDOMWindow_GetLocation(window->nswindow, &nslocation);
1984     if(FAILED(nsres) || !nslocation)
1985         return E_FAIL;
1986
1987     hres = IUri_GetFragment(uri, &frag);
1988     if(FAILED(hres)) {
1989         nsIDOMLocation_Release(nslocation);
1990         return hres;
1991     }
1992
1993     nsAString_InitDepend(&nsfrag_str, frag);
1994     nsres = nsIDOMLocation_SetHash(nslocation, &nsfrag_str);
1995     nsAString_Finish(&nsfrag_str);
1996     nsIDOMLocation_Release(nslocation);
1997     if(NS_FAILED(nsres))
1998         ERR("SetHash failed: %08x\n", nsres);
1999
2000     /*
2001      * IE supports scrolling to anchor elements with "#hash" ids (note that '#' is part of the id),
2002      * while Gecko scrolls only to elements with "hash" ids. We scroll the page ourselves if
2003      * a[id="#hash"] element can be found.
2004      */
2005     selector = heap_alloc(sizeof(selector_formatW)+SysStringLen(frag)*sizeof(WCHAR));
2006     if(selector) {
2007         nsIDOMElement *nselem = NULL;
2008         nsAString selector_str;
2009
2010         sprintfW(selector, selector_formatW, frag);
2011         nsAString_InitDepend(&selector_str, selector);
2012         /* NOTE: Gecko doesn't set result to NULL if there is no match, so nselem must be initialized */
2013         nsres = nsIDOMNodeSelector_QuerySelector(window->base.inner_window->doc->nsnode_selector, &selector_str, &nselem);
2014         nsAString_Finish(&selector_str);
2015         heap_free(selector);
2016         if(NS_SUCCEEDED(nsres) && nselem) {
2017             nsIDOMHTMLElement *html_elem;
2018
2019             nsres = nsIDOMElement_QueryInterface(nselem, &IID_nsIDOMHTMLElement, (void**)&html_elem);
2020             nsIDOMElement_Release(nselem);
2021             if(NS_SUCCEEDED(nsres)) {
2022                 nsIDOMHTMLElement_ScrollIntoView(html_elem, TRUE, 1);
2023                 nsIDOMHTMLElement_Release(html_elem);
2024             }
2025         }
2026     }
2027
2028     SysFreeString(frag);
2029
2030     if(window->doc_obj->doc_object_service) {
2031         IDocObjectService_FireNavigateComplete2(window->doc_obj->doc_object_service, &window->base.IHTMLWindow2_iface, 0x10);
2032         IDocObjectService_FireDocumentComplete(window->doc_obj->doc_object_service, &window->base.IHTMLWindow2_iface, 0);
2033
2034     }
2035
2036     return S_OK;
2037 }
2038
2039 HRESULT super_navigate(HTMLOuterWindow *window, IUri *uri, const WCHAR *headers, BYTE *post_data, DWORD post_data_size)
2040 {
2041     nsChannelBSC *bsc;
2042     IMoniker *mon;
2043     DWORD scheme;
2044     HRESULT hres;
2045
2046     if(window->doc_obj->client) {
2047         IOleCommandTarget *cmdtrg;
2048
2049         hres = IOleClientSite_QueryInterface(window->doc_obj->client, &IID_IOleCommandTarget, (void**)&cmdtrg);
2050         if(SUCCEEDED(hres)) {
2051             VARIANT in, out;
2052             BSTR url_str;
2053
2054             hres = IUri_GetDisplayUri(uri, &url_str);
2055             if(SUCCEEDED(hres)) {
2056                 V_VT(&in) = VT_BSTR;
2057                 V_BSTR(&in) = url_str;
2058                 V_VT(&out) = VT_BOOL;
2059                 V_BOOL(&out) = VARIANT_TRUE;
2060                 hres = IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 67, 0, &in, &out);
2061                 IOleCommandTarget_Release(cmdtrg);
2062                 if(SUCCEEDED(hres))
2063                     VariantClear(&out);
2064                 SysFreeString(url_str);
2065             }
2066         }
2067     }
2068
2069     if(window->uri && !post_data_size && compare_ignoring_frag(window->uri, uri)) {
2070         TRACE("fragment navigate\n");
2071         return navigate_fragment(window, uri);
2072     }
2073
2074     hres = CreateURLMonikerEx2(NULL, uri, &mon, URL_MK_UNIFORM);
2075     if(FAILED(hres))
2076         return hres;
2077
2078     /* FIXME: Why not set_ready_state? */
2079     window->readystate = READYSTATE_UNINITIALIZED;
2080
2081     hres = create_channelbsc(mon, headers, post_data, post_data_size, TRUE, &bsc);
2082     if(FAILED(hres)) {
2083         IMoniker_Release(mon);
2084         return hres;
2085     }
2086
2087     prepare_for_binding(&window->doc_obj->basedoc, mon, TRUE);
2088
2089     hres = IUri_GetScheme(uri, &scheme);
2090     if(SUCCEEDED(hres) && scheme != URL_SCHEME_JAVASCRIPT) {
2091         navigate_task_t *task;
2092
2093         task = heap_alloc(sizeof(*task));
2094         if(!task) {
2095             IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
2096             IMoniker_Release(mon);
2097             return E_OUTOFMEMORY;
2098         }
2099
2100         /* Silently and repeated when real loading starts? */
2101         window->readystate = READYSTATE_LOADING;
2102         call_docview_84(window->doc_obj);
2103
2104         task->window = window;
2105         task->bscallback = bsc;
2106         task->mon = mon;
2107         push_task(&task->header, navigate_proc, navigate_task_destr, window->task_magic);
2108
2109     }else {
2110         navigate_javascript_task_t *task;
2111
2112         IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
2113         IMoniker_Release(mon);
2114
2115         task = heap_alloc(sizeof(*task));
2116         if(!task)
2117             return E_OUTOFMEMORY;
2118
2119         /* Why silently? */
2120         window->readystate = READYSTATE_COMPLETE;
2121         call_docview_84(window->doc_obj);
2122
2123         IUri_AddRef(uri);
2124         task->window = window;
2125         task->uri = uri;
2126         push_task(&task->header, navigate_javascript_proc, navigate_javascript_task_destr, window->task_magic);
2127     }
2128
2129     return S_OK;
2130 }
2131
2132 HRESULT navigate_new_window(HTMLOuterWindow *window, IUri *uri, const WCHAR *name, IHTMLWindow2 **ret)
2133 {
2134     IWebBrowser2 *web_browser;
2135     IHTMLWindow2 *new_window;
2136     IBindCtx *bind_ctx;
2137     nsChannelBSC *bsc;
2138     HRESULT hres;
2139
2140     hres = create_channelbsc(NULL, NULL, NULL, 0, FALSE, &bsc);
2141     if(FAILED(hres))
2142         return hres;
2143
2144     hres = CreateAsyncBindCtx(0, &bsc->bsc.IBindStatusCallback_iface, NULL, &bind_ctx);
2145     if(FAILED(hres)) {
2146         IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
2147         return hres;
2148     }
2149
2150     hres = CoCreateInstance(&CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER,
2151             &IID_IWebBrowser2, (void**)&web_browser);
2152     if(SUCCEEDED(hres)) {
2153         ITargetFramePriv2 *target_frame_priv;
2154
2155         hres = IWebBrowser2_QueryInterface(web_browser, &IID_ITargetFramePriv2, (void**)&target_frame_priv);
2156         if(SUCCEEDED(hres)) {
2157             hres = ITargetFramePriv2_AggregatedNavigation2(target_frame_priv,
2158                     HLNF_DISABLEWINDOWRESTRICTIONS|HLNF_OPENINNEWWINDOW, bind_ctx, &bsc->bsc.IBindStatusCallback_iface,
2159                     name, uri, emptyW);
2160             ITargetFramePriv2_Release(target_frame_priv);
2161
2162             if(SUCCEEDED(hres))
2163                 hres = do_query_service((IUnknown*)web_browser, &SID_SHTMLWindow, &IID_IHTMLWindow2, (void**)&new_window);
2164         }
2165         if(FAILED(hres)) {
2166             IWebBrowser2_Quit(web_browser);
2167             IWebBrowser2_Release(web_browser);
2168         }
2169     }else {
2170         WARN("Could not create InternetExplorer instance: %08x\n", hres);
2171     }
2172
2173     IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
2174     IBindCtx_Release(bind_ctx);
2175     if(FAILED(hres))
2176         return hres;
2177
2178     IWebBrowser2_put_Visible(web_browser, VARIANT_TRUE);
2179     IWebBrowser2_Release(web_browser);
2180
2181     if(ret)
2182         *ret = new_window;
2183     else
2184         IHTMLWindow2_Release(new_window);
2185     return S_OK;
2186 }
2187
2188 HRESULT hlink_frame_navigate(HTMLDocument *doc, LPCWSTR url, nsChannel *nschannel, DWORD hlnf, BOOL *cancel)
2189 {
2190     IHlinkFrame *hlink_frame;
2191     nsChannelBSC *callback;
2192     IBindCtx *bindctx;
2193     IMoniker *mon;
2194     IHlink *hlink;
2195     HRESULT hres;
2196
2197     *cancel = FALSE;
2198
2199     hres = do_query_service((IUnknown*)doc->doc_obj->client, &IID_IHlinkFrame, &IID_IHlinkFrame,
2200             (void**)&hlink_frame);
2201     if(FAILED(hres))
2202         return S_OK;
2203
2204     hres = create_channelbsc(NULL, NULL, NULL, 0, FALSE, &callback);
2205     if(FAILED(hres)) {
2206         IHlinkFrame_Release(hlink_frame);
2207         return hres;
2208     }
2209
2210     if(nschannel)
2211         read_post_data_stream(callback, nschannel);
2212
2213     hres = CreateAsyncBindCtx(0, &callback->bsc.IBindStatusCallback_iface, NULL, &bindctx);
2214     if(SUCCEEDED(hres))
2215         hres = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER,
2216                 &IID_IHlink, (LPVOID*)&hlink);
2217
2218     if(SUCCEEDED(hres))
2219         hres = CreateURLMoniker(NULL, url, &mon);
2220
2221     if(SUCCEEDED(hres)) {
2222         IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET, mon, NULL);
2223
2224         if(hlnf & HLNF_OPENINNEWWINDOW) {
2225             static const WCHAR wszBlank[] = {'_','b','l','a','n','k',0};
2226             IHlink_SetTargetFrameName(hlink, wszBlank); /* FIXME */
2227         }
2228
2229         hres = IHlinkFrame_Navigate(hlink_frame, hlnf, bindctx,
2230                 &callback->bsc.IBindStatusCallback_iface, hlink);
2231         IMoniker_Release(mon);
2232         *cancel = hres == S_OK;
2233         hres = S_OK;
2234     }
2235
2236     IHlinkFrame_Release(hlink_frame);
2237     IBindCtx_Release(bindctx);
2238     IBindStatusCallback_Release(&callback->bsc.IBindStatusCallback_iface);
2239     return hres;
2240 }
2241
2242 static HRESULT navigate_uri(HTMLOuterWindow *window, IUri *uri, const WCHAR *display_uri)
2243 {
2244     nsWineURI *nsuri;
2245     HRESULT hres;
2246
2247     if(window->doc_obj && window->doc_obj->is_webbrowser && window == window->doc_obj->basedoc.window) {
2248         BOOL cancel = FALSE;
2249
2250         hres = IDocObjectService_FireBeforeNavigate2(window->doc_obj->doc_object_service, NULL, display_uri, 0x40,
2251                 NULL, NULL, 0, NULL, TRUE, &cancel);
2252         if(SUCCEEDED(hres) && cancel) {
2253             TRACE("Navigation canceled\n");
2254             return S_OK;
2255         }
2256
2257         return super_navigate(window, uri, NULL, NULL, 0);
2258     }
2259
2260     if(window->doc_obj && window == window->doc_obj->basedoc.window) {
2261         BOOL cancel;
2262
2263         hres = hlink_frame_navigate(&window->base.inner_window->doc->basedoc, display_uri, NULL, 0, &cancel);
2264         if(FAILED(hres))
2265             return hres;
2266
2267         if(cancel) {
2268             TRACE("Navigation handled by hlink frame\n");
2269             return S_OK;
2270         }
2271     }
2272
2273     hres = create_doc_uri(window, display_uri, &nsuri);
2274     if(FAILED(hres))
2275         return hres;
2276
2277     hres = load_nsuri(window, nsuri, NULL, LOAD_FLAGS_NONE);
2278     nsISupports_Release((nsISupports*)nsuri);
2279     return hres;
2280 }
2281
2282 HRESULT navigate_url(HTMLOuterWindow *window, const WCHAR *new_url, IUri *base_uri)
2283 {
2284     BSTR display_uri;
2285     IUri *uri;
2286     HRESULT hres;
2287
2288     if(new_url && base_uri)
2289         hres = CoInternetCombineUrlEx(base_uri, new_url, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO,
2290                 &uri, 0);
2291     else
2292         hres = CreateUri(new_url, 0, 0, &uri);
2293     if(FAILED(hres))
2294         return hres;
2295
2296     hres = IUri_GetDisplayUri(uri, &display_uri);
2297     if(FAILED(hres)) {
2298         IUri_Release(uri);
2299         return hres;
2300     }
2301
2302     if(window->doc_obj && window->doc_obj->hostui) {
2303         OLECHAR *translated_url = NULL;
2304
2305         hres = IDocHostUIHandler_TranslateUrl(window->doc_obj->hostui, 0, display_uri,
2306                 &translated_url);
2307         if(hres == S_OK) {
2308             TRACE("%08x %s -> %s\n", hres, debugstr_w(display_uri), debugstr_w(translated_url));
2309             SysFreeString(display_uri);
2310             IUri_Release(uri);
2311             hres = CreateUri(translated_url, 0, 0, &uri);
2312             CoTaskMemFree(translated_url);
2313             if(FAILED(hres))
2314                 return hres;
2315
2316             hres = IUri_GetDisplayUri(uri, &display_uri);
2317             if(FAILED(hres)) {
2318                 IUri_Release(uri);
2319                 return hres;
2320             }
2321         }
2322     }
2323
2324     hres = navigate_uri(window, uri, display_uri);
2325
2326     IUri_Release(uri);
2327     SysFreeString(display_uri);
2328     return hres;
2329 }