rpcrt4: Implement RpcCancelThread for the ncacn_ip_tcp protocol sequence.
[wine] / dlls / mshtml / persist.c
1 /*
2  * Copyright 2005 Jacek Caban
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "shlguid.h"
33 #include "idispids.h"
34
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37
38 #include "mshtml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
41
42 #define USER_AGENT "User-Agent:"
43 #define CONTENT_TYPE "Content-Type:"
44
45 static int fix_headers(char *buf, DWORD post_len)
46 {
47     char *ptr = NULL;
48
49     if(!strncasecmp(USER_AGENT, buf, sizeof(USER_AGENT)-1)) {
50         ptr = buf;
51     }else {
52         ptr = strstr(buf, "\r\n" USER_AGENT);
53         if(ptr)
54             ptr += 2;
55     }
56
57     if(ptr) {
58         const char *ptr2;
59
60         FIXME("Ignoring User-Agent header\n");
61
62         ptr2 = strstr(ptr, "\r\n");
63         if(ptr2)
64             memmove(ptr, ptr2, strlen(ptr2)+1);
65     }
66
67     if(!post_len) {
68         if(!strncasecmp(CONTENT_TYPE, buf, sizeof(CONTENT_TYPE)-1)) {
69             ptr = buf;
70         }else {
71             ptr = strstr(buf, "\r\n" CONTENT_TYPE);
72             if(ptr)
73                 ptr += 2;
74         }
75
76         if(ptr) {
77             const char *ptr2;
78
79             TRACE("Ignoring Content-Type header\n");
80
81             ptr2 = strstr(ptr, "\r\n");
82             if(ptr2)
83                 memmove(ptr, ptr2, strlen(ptr2)+1);
84         }
85     }
86
87     return strlen(buf);
88 }
89
90 static nsIInputStream *get_post_data_stream(IBindCtx *bctx)
91 {
92     nsIInputStream *ret = NULL;
93     IBindStatusCallback *callback;
94     IHttpNegotiate *http_negotiate;
95     BINDINFO bindinfo;
96     DWORD bindf = 0;
97     DWORD post_len = 0, headers_len = 0;
98     LPWSTR headers = NULL;
99     WCHAR emptystr[] = {0};
100     char *data;
101     HRESULT hres;
102
103     static WCHAR _BSCB_Holder_[] =
104         {'_','B','S','C','B','_','H','o','l','d','e','r','_',0};
105
106
107     /* FIXME: This should be done in URLMoniker */
108     if(!bctx)
109         return NULL;
110
111     hres = IBindCtx_GetObjectParam(bctx, _BSCB_Holder_, (IUnknown**)&callback);
112     if(FAILED(hres))
113         return NULL;
114
115     hres = IBindStatusCallback_QueryInterface(callback, &IID_IHttpNegotiate,
116                                               (void**)&http_negotiate);
117     if(SUCCEEDED(hres)) {
118         hres = IHttpNegotiate_BeginningTransaction(http_negotiate, emptystr,
119                                                    emptystr, 0, &headers);
120         IHttpNegotiate_Release(http_negotiate);
121
122         if(SUCCEEDED(hres) && headers)
123             headers_len = WideCharToMultiByte(CP_ACP, 0, headers, -1, NULL, 0, NULL, NULL);
124     }
125
126     memset(&bindinfo, 0, sizeof(bindinfo));
127     bindinfo.cbSize = sizeof(bindinfo);
128
129     hres = IBindStatusCallback_GetBindInfo(callback, &bindf, &bindinfo);
130
131     if(SUCCEEDED(hres) && bindinfo.dwBindVerb == BINDVERB_POST)
132         post_len = bindinfo.cbstgmedData;
133
134     if(headers_len || post_len) {
135         int len = 0;
136
137         static const char content_length[] = "Content-Length: %u\r\n\r\n";
138
139         data = mshtml_alloc(headers_len+post_len+sizeof(content_length)+8);
140
141         if(headers_len) {
142             WideCharToMultiByte(CP_ACP, 0, headers, -1, data, -1, NULL, NULL);
143             CoTaskMemFree(headers);
144             len = fix_headers(data, post_len);
145         }
146
147         if(post_len) {
148             if(len >= 4 && !strcmp(data+len-4, "\r\n\r\n"))
149                 len -= 2;
150
151             sprintf(data+len, content_length, post_len);
152             len = strlen(data);
153
154             memcpy(data+len, bindinfo.stgmedData.u.hGlobal, post_len);
155         }
156
157         TRACE("data = %s\n", debugstr_an(data, len+post_len));
158
159         ret = create_nsstream(data, len+post_len);
160     }
161
162     ReleaseBindInfo(&bindinfo);
163     IBindStatusCallback_Release(callback);
164
165     return ret;
166 }
167
168 void set_current_mon(HTMLDocument *This, IMoniker *mon)
169 {
170     HRESULT hres;
171
172     if(This->mon) {
173         IMoniker_Release(This->mon);
174         This->mon = NULL;
175     }
176
177     if(This->url) {
178         CoTaskMemFree(This->url);
179         This->url = NULL;
180     }
181
182     if(!mon)
183         return;
184
185     IMoniker_AddRef(mon);
186     This->mon = mon;
187
188     hres = IMoniker_GetDisplayName(mon, NULL, NULL, &This->url);
189     if(FAILED(hres))
190         WARN("GetDisplayName failed: %08x\n", hres);
191 }
192
193 static HRESULT set_moniker(HTMLDocument *This, IMoniker *mon, IBindCtx *pibc, BOOL *bind_complete)
194 {
195     BSCallback *bscallback;
196     LPOLESTR url = NULL;
197     task_t *task;
198     HRESULT hres;
199     nsresult nsres;
200
201     if(pibc) {
202         IUnknown *unk = NULL;
203
204         /* FIXME:
205          * Use params:
206          * "__PrecreatedObject"
207          * "BIND_CONTEXT_PARAM"
208          * "__HTMLLOADOPTIONS"
209          * "__DWNBINDINFO"
210          * "URL Context"
211          * "CBinding Context"
212          * "_ITransData_Object_"
213          * "_EnumFORMATETC_"
214          */
215
216         IBindCtx_GetObjectParam(pibc, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM, &unk);
217         if(unk) {
218             IOleClientSite *client = NULL;
219
220             hres = IUnknown_QueryInterface(unk, &IID_IOleClientSite, (void**)&client);
221             if(SUCCEEDED(hres)) {
222                 TRACE("Got client site %p\n", client);
223                 IOleObject_SetClientSite(OLEOBJ(This), client);
224                 IOleClientSite_Release(client);
225             }
226
227             IUnknown_Release(unk);
228         }
229     }
230
231     This->readystate = READYSTATE_LOADING;
232     call_property_onchanged(&This->cp_propnotif, DISPID_READYSTATE);
233     update_doc(This, UPDATE_TITLE);
234
235     HTMLDocument_LockContainer(This, TRUE);
236     
237     hres = IMoniker_GetDisplayName(mon, pibc, NULL, &url);
238     if(FAILED(hres)) {
239         WARN("GetDiaplayName failed: %08x\n", hres);
240         return hres;
241     }
242
243     TRACE("got url: %s\n", debugstr_w(url));
244
245     set_current_mon(This, mon);
246
247     if(This->client) {
248         VARIANT silent, offline;
249         IOleCommandTarget *cmdtrg = NULL;
250
251         hres = get_client_disp_property(This->client, DISPID_AMBIENT_SILENT, &silent);
252         if(SUCCEEDED(hres)) {
253             if(V_VT(&silent) != VT_BOOL)
254                 WARN("V_VT(silent) = %d\n", V_VT(&silent));
255             else if(V_BOOL(&silent))
256                 FIXME("silent == true\n");
257         }
258
259         hres = get_client_disp_property(This->client,
260                 DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &offline);
261         if(SUCCEEDED(hres)) {
262             if(V_VT(&silent) != VT_BOOL)
263                 WARN("V_VT(offline) = %d\n", V_VT(&silent));
264             else if(V_BOOL(&silent))
265                 FIXME("offline == true\n");
266         }
267
268         hres = IOleClientSite_QueryInterface(This->client, &IID_IOleCommandTarget,
269                 (void**)&cmdtrg);
270         if(SUCCEEDED(hres)) {
271             VARIANT var;
272
273             V_VT(&var) = VT_I4;
274             V_I4(&var) = 0;
275             IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);
276
277             IOleCommandTarget_Release(cmdtrg);
278         }
279     }
280
281     bscallback = create_bscallback(mon);
282
283     if(This->frame) {
284         task = mshtml_alloc(sizeof(task_t));
285
286         task->doc = This;
287         task->task_id = TASK_SETPROGRESS;
288         task->next = NULL;
289
290         push_task(task);
291     }
292
293     task = mshtml_alloc(sizeof(task_t));
294
295     task->doc = This;
296     task->task_id = TASK_SETDOWNLOADSTATE;
297     task->next = NULL;
298
299     push_task(task);
300
301     if(This->nscontainer) {
302         nsIInputStream *post_data_stream = get_post_data_stream(pibc);
303
304         This->nscontainer->bscallback = bscallback;
305         nsres = nsIWebNavigation_LoadURI(This->nscontainer->navigation, url,
306                 LOAD_FLAGS_NONE, NULL, post_data_stream, NULL);
307         This->nscontainer->bscallback = NULL;
308
309         if(post_data_stream)
310             nsIInputStream_Release(post_data_stream);
311
312         if(NS_SUCCEEDED(nsres)) {
313             /* FIXME: don't return here (URL Moniker needs to be good enough) */
314
315             IBindStatusCallback_Release(STATUSCLB(bscallback));
316             CoTaskMemFree(url);
317
318             if(bind_complete)
319                 *bind_complete = TRUE;
320             return S_OK;
321         }else if(nsres != WINE_NS_LOAD_FROM_MONIKER) {
322             WARN("LoadURI failed: %08x\n", nsres);
323         }
324     }
325
326     set_document_bscallback(This, bscallback);
327     IBindStatusCallback_Release(STATUSCLB(bscallback));
328     CoTaskMemFree(url);
329
330     if(bind_complete)
331         *bind_complete = FALSE;
332     return S_OK;
333 }
334
335 static HRESULT get_doc_string(HTMLDocument *This, char **str, DWORD *len)
336 {
337     nsIDOMDocument *nsdoc;
338     nsIDOMNode *nsnode;
339     LPCWSTR strw;
340     nsAString nsstr;
341     nsresult nsres;
342
343     if(!This->nscontainer) {
344         WARN("no nscontainer, returning NULL\n");
345         return S_OK;
346     }
347
348     nsres = nsIWebNavigation_GetDocument(This->nscontainer->navigation, &nsdoc);
349     if(NS_FAILED(nsres)) {
350         ERR("GetDocument failed: %08x\n", nsres);
351         return E_FAIL;
352     }
353
354     nsres = nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMNode, (void**)&nsnode);
355     nsIDOMDocument_Release(nsdoc);
356     if(NS_FAILED(nsres)) {
357         ERR("Could not get nsIDOMNode failed: %08x\n", nsres);
358         return E_FAIL;
359     }
360
361     nsAString_Init(&nsstr, NULL);
362     nsnode_to_nsstring(nsnode, &nsstr);
363     nsIDOMNode_Release(nsnode);
364
365     nsAString_GetData(&nsstr, &strw, NULL);
366     TRACE("%s\n", debugstr_w(strw));
367
368     *len = WideCharToMultiByte(CP_ACP, 0, strw, -1, NULL, 0, NULL, NULL);
369     *str = mshtml_alloc(*len);
370     WideCharToMultiByte(CP_ACP, 0, strw, -1, *str, *len, NULL, NULL);
371
372     nsAString_Finish(&nsstr);
373
374     return S_OK;
375 }
376
377
378 /**********************************************************
379  * IPersistMoniker implementation
380  */
381
382 #define PERSISTMON_THIS(iface) DEFINE_THIS(HTMLDocument, PersistMoniker, iface)
383
384 static HRESULT WINAPI PersistMoniker_QueryInterface(IPersistMoniker *iface, REFIID riid,
385                                                             void **ppvObject)
386 {
387     HTMLDocument *This = PERSISTMON_THIS(iface);
388     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
389 }
390
391 static ULONG WINAPI PersistMoniker_AddRef(IPersistMoniker *iface)
392 {
393     HTMLDocument *This = PERSISTMON_THIS(iface);
394     return IHTMLDocument2_AddRef(HTMLDOC(This));
395 }
396
397 static ULONG WINAPI PersistMoniker_Release(IPersistMoniker *iface)
398 {
399     HTMLDocument *This = PERSISTMON_THIS(iface);
400     return IHTMLDocument2_Release(HTMLDOC(This));
401 }
402
403 static HRESULT WINAPI PersistMoniker_GetClassID(IPersistMoniker *iface, CLSID *pClassID)
404 {
405     HTMLDocument *This = PERSISTMON_THIS(iface);
406     return IPersist_GetClassID(PERSIST(This), pClassID);
407 }
408
409 static HRESULT WINAPI PersistMoniker_IsDirty(IPersistMoniker *iface)
410 {
411     HTMLDocument *This = PERSISTMON_THIS(iface);
412
413     TRACE("(%p)\n", This);
414
415     return IPersistStreamInit_IsDirty(PERSTRINIT(This));
416 }
417
418 static HRESULT WINAPI PersistMoniker_Load(IPersistMoniker *iface, BOOL fFullyAvailable,
419         IMoniker *pimkName, LPBC pibc, DWORD grfMode)
420 {
421     HTMLDocument *This = PERSISTMON_THIS(iface);
422     BOOL bind_complete = FALSE;
423     HRESULT hres;
424
425     TRACE("(%p)->(%x %p %p %08x)\n", This, fFullyAvailable, pimkName, pibc, grfMode);
426
427     hres = set_moniker(This, pimkName, pibc, &bind_complete);
428     if(FAILED(hres))
429         return hres;
430
431     if(!bind_complete)
432         return start_binding(This, This->bscallback);
433
434     return S_OK;
435 }
436
437 static HRESULT WINAPI PersistMoniker_Save(IPersistMoniker *iface, IMoniker *pimkName,
438         LPBC pbc, BOOL fRemember)
439 {
440     HTMLDocument *This = PERSISTMON_THIS(iface);
441     FIXME("(%p)->(%p %p %x)\n", This, pimkName, pbc, fRemember);
442     return E_NOTIMPL;
443 }
444
445 static HRESULT WINAPI PersistMoniker_SaveCompleted(IPersistMoniker *iface, IMoniker *pimkName, LPBC pibc)
446 {
447     HTMLDocument *This = PERSISTMON_THIS(iface);
448     FIXME("(%p)->(%p %p)\n", This, pimkName, pibc);
449     return E_NOTIMPL;
450 }
451
452 static HRESULT WINAPI PersistMoniker_GetCurMoniker(IPersistMoniker *iface, IMoniker **ppimkName)
453 {
454     HTMLDocument *This = PERSISTMON_THIS(iface);
455
456     TRACE("(%p)->(%p)\n", This, ppimkName);
457
458     if(!This->mon)
459         return E_UNEXPECTED;
460
461     IMoniker_AddRef(This->mon);
462     *ppimkName = This->mon;
463     return S_OK;
464 }
465
466 static const IPersistMonikerVtbl PersistMonikerVtbl = {
467     PersistMoniker_QueryInterface,
468     PersistMoniker_AddRef,
469     PersistMoniker_Release,
470     PersistMoniker_GetClassID,
471     PersistMoniker_IsDirty,
472     PersistMoniker_Load,
473     PersistMoniker_Save,
474     PersistMoniker_SaveCompleted,
475     PersistMoniker_GetCurMoniker
476 };
477
478 /**********************************************************
479  * IMonikerProp implementation
480  */
481
482 #define MONPROP_THIS(iface) DEFINE_THIS(HTMLDocument, MonikerProp, iface)
483
484 static HRESULT WINAPI MonikerProp_QueryInterface(IMonikerProp *iface, REFIID riid, void **ppvObject)
485 {
486     HTMLDocument *This = MONPROP_THIS(iface);
487     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
488 }
489
490 static ULONG WINAPI MonikerProp_AddRef(IMonikerProp *iface)
491 {
492     HTMLDocument *This = MONPROP_THIS(iface);
493     return IHTMLDocument2_AddRef(HTMLDOC(This));
494 }
495
496 static ULONG WINAPI MonikerProp_Release(IMonikerProp *iface)
497 {
498     HTMLDocument *This = MONPROP_THIS(iface);
499     return IHTMLDocument_Release(HTMLDOC(This));
500 }
501
502 static HRESULT WINAPI MonikerProp_PutProperty(IMonikerProp *iface, MONIKERPROPERTY mkp, LPCWSTR val)
503 {
504     HTMLDocument *This = MONPROP_THIS(iface);
505     FIXME("(%p)->(%d %s)\n", This, mkp, debugstr_w(val));
506     return E_NOTIMPL;
507 }
508
509 static const IMonikerPropVtbl MonikerPropVtbl = {
510     MonikerProp_QueryInterface,
511     MonikerProp_AddRef,
512     MonikerProp_Release,
513     MonikerProp_PutProperty
514 };
515
516 /**********************************************************
517  * IPersistFile implementation
518  */
519
520 #define PERSISTFILE_THIS(iface) DEFINE_THIS(HTMLDocument, PersistFile, iface)
521
522 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *iface, REFIID riid, void **ppvObject)
523 {
524     HTMLDocument *This = PERSISTFILE_THIS(iface);
525     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
526 }
527
528 static ULONG WINAPI PersistFile_AddRef(IPersistFile *iface)
529 {
530     HTMLDocument *This = PERSISTFILE_THIS(iface);
531     return IHTMLDocument2_AddRef(HTMLDOC(This));
532 }
533
534 static ULONG WINAPI PersistFile_Release(IPersistFile *iface)
535 {
536     HTMLDocument *This = PERSISTFILE_THIS(iface);
537     return IHTMLDocument2_Release(HTMLDOC(This));
538 }
539
540 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *iface, CLSID *pClassID)
541 {
542     HTMLDocument *This = PERSISTFILE_THIS(iface);
543
544     TRACE("(%p)->(%p)\n", This, pClassID);
545
546     if(!pClassID)
547         return E_INVALIDARG;
548
549     memcpy(pClassID, &CLSID_HTMLDocument, sizeof(CLSID));
550     return S_OK;
551 }
552
553 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *iface)
554 {
555     HTMLDocument *This = PERSISTFILE_THIS(iface);
556
557     TRACE("(%p)\n", This);
558
559     return IPersistStreamInit_IsDirty(PERSTRINIT(This));
560 }
561
562 static HRESULT WINAPI PersistFile_Load(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
563 {
564     HTMLDocument *This = PERSISTFILE_THIS(iface);
565     FIXME("(%p)->(%s %08x)\n", This, debugstr_w(pszFileName), dwMode);
566     return E_NOTIMPL;
567 }
568
569 static HRESULT WINAPI PersistFile_Save(IPersistFile *iface, LPCOLESTR pszFileName, BOOL fRemember)
570 {
571     HTMLDocument *This = PERSISTFILE_THIS(iface);
572     char *str;
573     DWORD len, written=0;
574     HANDLE file;
575     HRESULT hres;
576
577     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pszFileName), fRemember);
578
579     file = CreateFileW(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
580                        FILE_ATTRIBUTE_NORMAL, NULL);
581     if(file == INVALID_HANDLE_VALUE) {
582         WARN("Could not create file: %u\n", GetLastError());
583         return E_FAIL;
584     }
585
586     hres = get_doc_string(This, &str, &len);
587     if(FAILED(hres))
588         return hres;
589
590     WriteFile(file, str, len, &written, NULL);
591     CloseHandle(file);
592     return S_OK;
593 }
594
595 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
596 {
597     HTMLDocument *This = PERSISTFILE_THIS(iface);
598     FIXME("(%p)->(%s)\n", This, debugstr_w(pszFileName));
599     return E_NOTIMPL;
600 }
601
602 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *iface, LPOLESTR *pszFileName)
603 {
604     HTMLDocument *This = PERSISTFILE_THIS(iface);
605     FIXME("(%p)->(%p)\n", This, pszFileName);
606     return E_NOTIMPL;
607 }
608
609 static const IPersistFileVtbl PersistFileVtbl = {
610     PersistFile_QueryInterface,
611     PersistFile_AddRef,
612     PersistFile_Release,
613     PersistFile_GetClassID,
614     PersistFile_IsDirty,
615     PersistFile_Load,
616     PersistFile_Save,
617     PersistFile_SaveCompleted,
618     PersistFile_GetCurFile
619 };
620
621 #define PERSTRINIT_THIS(iface) DEFINE_THIS(HTMLDocument, PersistStreamInit, iface)
622
623 static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface,
624                                                        REFIID riid, void **ppv)
625 {
626     HTMLDocument *This = PERSTRINIT_THIS(iface);
627     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppv);
628 }
629
630 static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
631 {
632     HTMLDocument *This = PERSTRINIT_THIS(iface);
633     return IHTMLDocument2_AddRef(HTMLDOC(This));
634 }
635
636 static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
637 {
638     HTMLDocument *This = PERSTRINIT_THIS(iface);
639     return IHTMLDocument2_Release(HTMLDOC(This));
640 }
641
642 static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *pClassID)
643 {
644     HTMLDocument *This = PERSTRINIT_THIS(iface);
645     return IPersist_GetClassID(PERSIST(This), pClassID);
646 }
647
648 static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
649 {
650     HTMLDocument *This = PERSTRINIT_THIS(iface);
651
652     TRACE("(%p)\n", This);
653
654     if(This->usermode == EDITMODE)
655         return editor_is_dirty(This);
656
657     return S_FALSE;
658 }
659
660 static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, LPSTREAM pStm)
661 {
662     HTMLDocument *This = PERSTRINIT_THIS(iface);
663     IMoniker *mon;
664     HRESULT hres;
665
666     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
667
668     TRACE("(%p)->(%p)\n", This, pStm);
669
670     hres = CreateURLMoniker(NULL, about_blankW, &mon);
671     if(FAILED(hres)) {
672         WARN("CreateURLMoniker failed: %08x\n", hres);
673         return hres;
674     }
675
676     hres = set_moniker(This, mon, NULL, NULL);
677     IMoniker_Release(mon);
678     if(FAILED(hres))
679         return hres;
680
681     return load_stream(This->bscallback, pStm);
682 }
683
684 static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, LPSTREAM pStm,
685                                              BOOL fClearDirty)
686 {
687     HTMLDocument *This = PERSTRINIT_THIS(iface);
688     char *str;
689     DWORD len, written=0;
690     HRESULT hres;
691
692     WARN("(%p)->(%p %x) needs more work\n", This, pStm, fClearDirty);
693
694     hres = get_doc_string(This, &str, &len);
695     if(FAILED(hres))
696         return hres;
697
698
699     hres = IStream_Write(pStm, str, len, &written);
700     if(FAILED(hres))
701         FIXME("Write failed: %08x\n", hres);
702
703     mshtml_free(str);
704     return S_OK;
705 }
706
707 static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface,
708                                                    ULARGE_INTEGER *pcbSize)
709 {
710     HTMLDocument *This = PERSTRINIT_THIS(iface);
711     FIXME("(%p)->(%p)\n", This, pcbSize);
712     return E_NOTIMPL;
713 }
714
715 static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
716 {
717     HTMLDocument *This = PERSTRINIT_THIS(iface);
718     FIXME("(%p)\n", This);
719     return E_NOTIMPL;
720 }
721
722 #undef PERSTRINIT_THIS
723
724 static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
725     PersistStreamInit_QueryInterface,
726     PersistStreamInit_AddRef,
727     PersistStreamInit_Release,
728     PersistStreamInit_GetClassID,
729     PersistStreamInit_IsDirty,
730     PersistStreamInit_Load,
731     PersistStreamInit_Save,
732     PersistStreamInit_GetSizeMax,
733     PersistStreamInit_InitNew
734 };
735
736 void HTMLDocument_Persist_Init(HTMLDocument *This)
737 {
738     This->lpPersistMonikerVtbl = &PersistMonikerVtbl;
739     This->lpPersistFileVtbl = &PersistFileVtbl;
740     This->lpMonikerPropVtbl = &MonikerPropVtbl;
741     This->lpPersistStreamInitVtbl = &PersistStreamInitVtbl;
742
743     This->bscallback = NULL;
744     This->mon = NULL;
745     This->url = NULL;
746 }