mshtml: Use flags in navigation functions.
[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 #define NO_SHLWAPI_REG
36 #include "shlwapi.h"
37
38 #include "wine/debug.h"
39
40 #include "mshtml_private.h"
41 #include "htmlscript.h"
42 #include "htmlevent.h"
43 #include "binding.h"
44 #include "resource.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
47
48 /* Undocumented notification, see tests */
49 #define CMDID_EXPLORER_UPDATEHISTORY 38
50
51 typedef struct {
52     task_t header;
53     HTMLDocumentObj *doc;
54     BOOL set_download;
55     LPOLESTR url;
56 } download_proc_task_t;
57
58 static BOOL use_gecko_script(HTMLOuterWindow *window)
59 {
60     DWORD zone;
61     HRESULT hres;
62
63     hres = IInternetSecurityManager_MapUrlToZone(window->secmgr, window->url, &zone, 0);
64     if(FAILED(hres)) {
65         WARN("Could not map %s to zone: %08x\n", debugstr_w(window->url), hres);
66         return TRUE;
67     }
68
69     TRACE("zone %d\n", zone);
70     return zone == URLZONE_UNTRUSTED;
71 }
72
73 static void notify_travellog_update(HTMLDocumentObj *doc)
74 {
75     IOleCommandTarget *cmdtrg;
76     HRESULT hres;
77
78     if(!doc->is_webbrowser)
79         return;
80
81     /* Don't notify if we were in about: page */
82     if(doc->basedoc.window->uri) {
83         DWORD scheme;
84
85         hres = IUri_GetScheme(doc->basedoc.window->uri, &scheme);
86         if(SUCCEEDED(hres) && scheme == URL_SCHEME_ABOUT)
87             return;
88     }
89
90     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&cmdtrg);
91     if(SUCCEEDED(hres)) {
92         VARIANT vin;
93
94         V_VT(&vin) = VT_I4;
95         V_I4(&vin) = 0;
96
97         IOleCommandTarget_Exec(cmdtrg, &CGID_Explorer, CMDID_EXPLORER_UPDATEHISTORY, 0, &vin, NULL);
98         IOleCommandTarget_Release(cmdtrg);
99     }
100 }
101
102 void set_current_uri(HTMLOuterWindow *window, IUri *uri)
103 {
104     if(window->uri) {
105         IUri_Release(window->uri);
106         window->uri = NULL;
107     }
108
109     if(window->uri_nofrag) {
110         IUri_Release(window->uri_nofrag);
111         window->uri_nofrag = NULL;
112     }
113
114     SysFreeString(window->url);
115     window->url = NULL;
116
117     if(!uri)
118         return;
119
120     IUri_AddRef(uri);
121     window->uri = uri;
122
123     window->uri_nofrag = get_uri_nofrag(uri);
124     if(!window->uri_nofrag) {
125         FIXME("get_uri_nofrag failed\n");
126         IUri_AddRef(uri);
127         window->uri_nofrag = uri;
128     }
129
130     IUri_GetDisplayUri(uri, &window->url);
131 }
132
133 void set_current_mon(HTMLOuterWindow *This, IMoniker *mon)
134 {
135     IUriContainer *uri_container;
136     IUri *uri = NULL;
137     HRESULT hres;
138
139     if(This->mon) {
140         if(This->doc_obj)
141             notify_travellog_update(This->doc_obj);
142         IMoniker_Release(This->mon);
143         This->mon = NULL;
144     }
145
146     if(!mon)
147         return;
148
149     IMoniker_AddRef(mon);
150     This->mon = mon;
151
152     hres = IMoniker_QueryInterface(mon, &IID_IUriContainer, (void**)&uri_container);
153     if(SUCCEEDED(hres)) {
154         hres = IUriContainer_GetIUri(uri_container, &uri);
155         IUriContainer_Release(uri_container);
156         if(hres != S_OK) {
157             WARN("GetIUri failed: %08x\n", hres);
158             uri = NULL;
159         }
160     }
161
162     if(!uri) {
163         WCHAR *url;
164
165         hres = IMoniker_GetDisplayName(mon, NULL, NULL, &url);
166         if(SUCCEEDED(hres)) {
167             hres = CreateUri(url, 0, 0, &uri);
168             if(FAILED(hres)) {
169                 WARN("CrateUri failed: %08x\n", hres);
170                 set_current_uri(This, NULL);
171                 This->url = SysAllocString(url);
172                 CoTaskMemFree(url);
173                 return;
174             }
175             CoTaskMemFree(url);
176         }else {
177             WARN("GetDisplayName failed: %08x\n", hres);
178         }
179     }
180
181     set_current_uri(This, uri);
182     if(uri)
183         IUri_Release(uri);
184     set_script_mode(This, use_gecko_script(This) ? SCRIPTMODE_GECKO : SCRIPTMODE_ACTIVESCRIPT);
185 }
186
187 HRESULT create_relative_uri(HTMLOuterWindow *window, const WCHAR *rel_uri, IUri **uri)
188 {
189     return window->uri
190         ? CoInternetCombineUrlEx(window->uri, rel_uri, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO, uri, 0)
191         : CreateUri(rel_uri, 0, 0, uri);
192 }
193
194 void set_download_state(HTMLDocumentObj *doc, int state)
195 {
196     if(doc->client) {
197         IOleCommandTarget *olecmd;
198         HRESULT hres;
199
200         hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
201         if(SUCCEEDED(hres)) {
202             VARIANT var;
203
204             V_VT(&var) = VT_I4;
205             V_I4(&var) = state;
206
207             IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETDOWNLOADSTATE,
208                     OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL);
209             IOleCommandTarget_Release(olecmd);
210         }
211     }
212
213     doc->download_state = state;
214 }
215
216 static void set_progress_proc(task_t *_task)
217 {
218     docobj_task_t *task = (docobj_task_t*)_task;
219     IOleCommandTarget *olecmd = NULL;
220     HTMLDocumentObj *doc = task->doc;
221     HRESULT hres;
222
223     TRACE("(%p)\n", doc);
224
225     if(doc->client)
226         IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
227
228     if(olecmd) {
229         VARIANT progress_max, progress;
230
231         V_VT(&progress_max) = VT_I4;
232         V_I4(&progress_max) = 0; /* FIXME */
233         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSMAX, OLECMDEXECOPT_DONTPROMPTUSER,
234                                &progress_max, NULL);
235
236         V_VT(&progress) = VT_I4;
237         V_I4(&progress) = 0; /* FIXME */
238         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS, OLECMDEXECOPT_DONTPROMPTUSER,
239                                &progress, NULL);
240         IOleCommandTarget_Release(olecmd);
241     }
242
243     if(doc->usermode == EDITMODE && doc->hostui) {
244         DOCHOSTUIINFO hostinfo;
245
246         memset(&hostinfo, 0, sizeof(DOCHOSTUIINFO));
247         hostinfo.cbSize = sizeof(DOCHOSTUIINFO);
248         hres = IDocHostUIHandler_GetHostInfo(doc->hostui, &hostinfo);
249         if(SUCCEEDED(hres))
250             /* FIXME: use hostinfo */
251             TRACE("hostinfo = {%u %08x %08x %s %s}\n",
252                     hostinfo.cbSize, hostinfo.dwFlags, hostinfo.dwDoubleClick,
253                     debugstr_w(hostinfo.pchHostCss), debugstr_w(hostinfo.pchHostNS));
254     }
255 }
256
257 static void set_downloading_proc(task_t *_task)
258 {
259     download_proc_task_t *task = (download_proc_task_t*)_task;
260     HTMLDocumentObj *doc = task->doc;
261     HRESULT hres;
262
263     TRACE("(%p)\n", doc);
264
265     set_statustext(doc, IDS_STATUS_DOWNLOADINGFROM, task->url);
266
267     if(task->set_download)
268         set_download_state(doc, 1);
269
270     if(!doc->client)
271         return;
272
273     if(doc->view_sink)
274         IAdviseSink_OnViewChange(doc->view_sink, DVASPECT_CONTENT, -1);
275
276     if(doc->hostui) {
277         IDropTarget *drop_target = NULL;
278
279         hres = IDocHostUIHandler_GetDropTarget(doc->hostui, NULL /* FIXME */, &drop_target);
280         if(SUCCEEDED(hres) && drop_target) {
281             FIXME("Use IDropTarget\n");
282             IDropTarget_Release(drop_target);
283         }
284     }
285 }
286
287 static void set_downloading_task_destr(task_t *_task)
288 {
289     download_proc_task_t *task = (download_proc_task_t*)_task;
290
291     CoTaskMemFree(task->url);
292     heap_free(task);
293 }
294
295 void prepare_for_binding(HTMLDocument *This, IMoniker *mon, DWORD flags)
296 {
297     HRESULT hres;
298
299     if(This->doc_obj->client) {
300         VARIANT silent, offline;
301
302         hres = get_client_disp_property(This->doc_obj->client, DISPID_AMBIENT_SILENT, &silent);
303         if(SUCCEEDED(hres)) {
304             if(V_VT(&silent) != VT_BOOL)
305                 WARN("silent = %s\n", debugstr_variant(&silent));
306             else if(V_BOOL(&silent))
307                 FIXME("silent == true\n");
308         }
309
310         hres = get_client_disp_property(This->doc_obj->client,
311                 DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &offline);
312         if(SUCCEEDED(hres)) {
313             if(V_VT(&offline) != VT_BOOL)
314                 WARN("offline = %s\n", debugstr_variant(&offline));
315             else if(V_BOOL(&offline))
316                 FIXME("offline == true\n");
317         }
318     }
319
320     if(This->window->mon) {
321         update_doc(This, UPDATE_TITLE|UPDATE_UI);
322     }else {
323         update_doc(This, UPDATE_TITLE);
324         set_current_mon(This->window, mon);
325     }
326
327     if(This->doc_obj->client) {
328         IOleCommandTarget *cmdtrg = NULL;
329
330         hres = IOleClientSite_QueryInterface(This->doc_obj->client, &IID_IOleCommandTarget,
331                 (void**)&cmdtrg);
332         if(SUCCEEDED(hres)) {
333             VARIANT var, out;
334
335             if(flags & BINDING_NAVIGATED) {
336                 V_VT(&var) = VT_UNKNOWN;
337                 V_UNKNOWN(&var) = (IUnknown*)&This->window->base.IHTMLWindow2_iface;
338                 V_VT(&out) = VT_EMPTY;
339                 hres = IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 63, 0, &var, &out);
340                 if(SUCCEEDED(hres))
341                     VariantClear(&out);
342             }else {
343                 V_VT(&var) = VT_I4;
344                 V_I4(&var) = 0;
345                 IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);
346             }
347
348             IOleCommandTarget_Release(cmdtrg);
349         }
350     }
351 }
352
353 HRESULT set_moniker(HTMLDocument *This, IMoniker *mon, IBindCtx *pibc, nsChannelBSC *async_bsc, BOOL set_download)
354 {
355     download_proc_task_t *download_task;
356     nsChannelBSC *bscallback;
357     nsWineURI *nsuri;
358     LPOLESTR url;
359     HRESULT hres;
360
361     hres = IMoniker_GetDisplayName(mon, pibc, NULL, &url);
362     if(FAILED(hres)) {
363         WARN("GetDiaplayName failed: %08x\n", hres);
364         return hres;
365     }
366
367     TRACE("got url: %s\n", debugstr_w(url));
368
369     set_ready_state(This->window, READYSTATE_LOADING);
370
371     hres = create_doc_uri(This->window, url, &nsuri);
372     if(SUCCEEDED(hres)) {
373         if(async_bsc)
374             bscallback = async_bsc;
375         else
376             hres = create_channelbsc(mon, NULL, NULL, 0, TRUE, &bscallback);
377     }
378
379     if(SUCCEEDED(hres)) {
380         remove_target_tasks(This->task_magic);
381         abort_window_bindings(This->window->base.inner_window);
382
383         hres = load_nsuri(This->window, nsuri, bscallback, 0/*LOAD_INITIAL_DOCUMENT_URI*/);
384         nsISupports_Release((nsISupports*)nsuri); /* FIXME */
385         if(SUCCEEDED(hres))
386             hres = create_pending_window(This->window, bscallback);
387         if(bscallback != async_bsc)
388             IBindStatusCallback_Release(&bscallback->bsc.IBindStatusCallback_iface);
389     }
390
391     if(FAILED(hres)) {
392         CoTaskMemFree(url);
393         return hres;
394     }
395
396     HTMLDocument_LockContainer(This->doc_obj, TRUE);
397
398     if(This->doc_obj->frame) {
399         docobj_task_t *task;
400
401         task = heap_alloc(sizeof(docobj_task_t));
402         task->doc = This->doc_obj;
403         hres = push_task(&task->header, set_progress_proc, NULL, This->doc_obj->basedoc.task_magic);
404         if(FAILED(hres)) {
405             CoTaskMemFree(url);
406             return hres;
407         }
408     }
409
410     download_task = heap_alloc(sizeof(download_proc_task_t));
411     download_task->doc = This->doc_obj;
412     download_task->set_download = set_download;
413     download_task->url = url;
414     return push_task(&download_task->header, set_downloading_proc, set_downloading_task_destr, This->doc_obj->basedoc.task_magic);
415 }
416
417 void set_ready_state(HTMLOuterWindow *window, READYSTATE readystate)
418 {
419     window->readystate = readystate;
420
421     if(window->doc_obj && window->doc_obj->basedoc.window == window)
422         call_property_onchanged(&window->doc_obj->basedoc.cp_propnotif, DISPID_READYSTATE);
423
424     fire_event(window->base.inner_window->doc, EVENTID_READYSTATECHANGE, FALSE,
425             window->base.inner_window->doc->node.nsnode, NULL, NULL);
426
427     if(window->frame_element)
428         fire_event(window->frame_element->element.node.doc, EVENTID_READYSTATECHANGE,
429                    TRUE, window->frame_element->element.node.nsnode, NULL, NULL);
430 }
431
432 static HRESULT get_doc_string(HTMLDocumentNode *This, char **str)
433 {
434     nsIDOMNode *nsnode;
435     LPCWSTR strw;
436     nsAString nsstr;
437     nsresult nsres;
438     HRESULT hres;
439
440     if(!This->nsdoc) {
441         WARN("NULL nsdoc\n");
442         return E_UNEXPECTED;
443     }
444
445     nsres = nsIDOMHTMLDocument_QueryInterface(This->nsdoc, &IID_nsIDOMNode, (void**)&nsnode);
446     if(NS_FAILED(nsres)) {
447         ERR("Could not get nsIDOMNode failed: %08x\n", nsres);
448         return E_FAIL;
449     }
450
451     nsAString_Init(&nsstr, NULL);
452     hres = nsnode_to_nsstring(nsnode, &nsstr);
453     nsIDOMNode_Release(nsnode);
454     if(FAILED(hres)) {
455         nsAString_Finish(&nsstr);
456         return hres;
457     }
458
459     nsAString_GetData(&nsstr, &strw);
460     TRACE("%s\n", debugstr_w(strw));
461
462     *str = heap_strdupWtoA(strw);
463
464     nsAString_Finish(&nsstr);
465
466     if(!*str)
467         return E_OUTOFMEMORY;
468     return S_OK;
469 }
470
471
472 /**********************************************************
473  * IPersistMoniker implementation
474  */
475
476 static inline HTMLDocument *impl_from_IPersistMoniker(IPersistMoniker *iface)
477 {
478     return CONTAINING_RECORD(iface, HTMLDocument, IPersistMoniker_iface);
479 }
480
481 static HRESULT WINAPI PersistMoniker_QueryInterface(IPersistMoniker *iface, REFIID riid, void **ppv)
482 {
483     HTMLDocument *This = impl_from_IPersistMoniker(iface);
484     return htmldoc_query_interface(This, riid, ppv);
485 }
486
487 static ULONG WINAPI PersistMoniker_AddRef(IPersistMoniker *iface)
488 {
489     HTMLDocument *This = impl_from_IPersistMoniker(iface);
490     return htmldoc_addref(This);
491 }
492
493 static ULONG WINAPI PersistMoniker_Release(IPersistMoniker *iface)
494 {
495     HTMLDocument *This = impl_from_IPersistMoniker(iface);
496     return htmldoc_release(This);
497 }
498
499 static HRESULT WINAPI PersistMoniker_GetClassID(IPersistMoniker *iface, CLSID *pClassID)
500 {
501     HTMLDocument *This = impl_from_IPersistMoniker(iface);
502     return IPersistFile_GetClassID(&This->IPersistFile_iface, pClassID);
503 }
504
505 static HRESULT WINAPI PersistMoniker_IsDirty(IPersistMoniker *iface)
506 {
507     HTMLDocument *This = impl_from_IPersistMoniker(iface);
508
509     TRACE("(%p)\n", This);
510
511     return IPersistStreamInit_IsDirty(&This->IPersistStreamInit_iface);
512 }
513
514 static HRESULT WINAPI PersistMoniker_Load(IPersistMoniker *iface, BOOL fFullyAvailable,
515         IMoniker *pimkName, LPBC pibc, DWORD grfMode)
516 {
517     HTMLDocument *This = impl_from_IPersistMoniker(iface);
518     HRESULT hres;
519
520     TRACE("(%p)->(%x %p %p %08x)\n", This, fFullyAvailable, pimkName, pibc, grfMode);
521
522     if(pibc) {
523         IUnknown *unk = NULL;
524
525         /* FIXME:
526          * Use params:
527          * "__PrecreatedObject"
528          * "BIND_CONTEXT_PARAM"
529          * "__HTMLLOADOPTIONS"
530          * "__DWNBINDINFO"
531          * "URL Context"
532          * "_ITransData_Object_"
533          * "_EnumFORMATETC_"
534          */
535
536         hres = IBindCtx_GetObjectParam(pibc, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM, &unk);
537         if(SUCCEEDED(hres) && unk) {
538             IOleClientSite *client = NULL;
539
540             hres = IUnknown_QueryInterface(unk, &IID_IOleClientSite, (void**)&client);
541             if(SUCCEEDED(hres)) {
542                 TRACE("Got client site %p\n", client);
543                 IOleObject_SetClientSite(&This->IOleObject_iface, client);
544                 IOleClientSite_Release(client);
545             }
546
547             IUnknown_Release(unk);
548         }
549     }
550
551     prepare_for_binding(This, pimkName, FALSE);
552     call_docview_84(This->doc_obj);
553     hres = set_moniker(This, pimkName, pibc, NULL, TRUE);
554     if(FAILED(hres))
555         return hres;
556
557     return start_binding(This->window->pending_window, (BSCallback*)This->window->pending_window->bscallback, pibc);
558 }
559
560 static HRESULT WINAPI PersistMoniker_Save(IPersistMoniker *iface, IMoniker *pimkName,
561         LPBC pbc, BOOL fRemember)
562 {
563     HTMLDocument *This = impl_from_IPersistMoniker(iface);
564     FIXME("(%p)->(%p %p %x)\n", This, pimkName, pbc, fRemember);
565     return E_NOTIMPL;
566 }
567
568 static HRESULT WINAPI PersistMoniker_SaveCompleted(IPersistMoniker *iface, IMoniker *pimkName, LPBC pibc)
569 {
570     HTMLDocument *This = impl_from_IPersistMoniker(iface);
571     FIXME("(%p)->(%p %p)\n", This, pimkName, pibc);
572     return E_NOTIMPL;
573 }
574
575 static HRESULT WINAPI PersistMoniker_GetCurMoniker(IPersistMoniker *iface, IMoniker **ppimkName)
576 {
577     HTMLDocument *This = impl_from_IPersistMoniker(iface);
578
579     TRACE("(%p)->(%p)\n", This, ppimkName);
580
581     if(!This->window || !This->window->mon)
582         return E_UNEXPECTED;
583
584     IMoniker_AddRef(This->window->mon);
585     *ppimkName = This->window->mon;
586     return S_OK;
587 }
588
589 static const IPersistMonikerVtbl PersistMonikerVtbl = {
590     PersistMoniker_QueryInterface,
591     PersistMoniker_AddRef,
592     PersistMoniker_Release,
593     PersistMoniker_GetClassID,
594     PersistMoniker_IsDirty,
595     PersistMoniker_Load,
596     PersistMoniker_Save,
597     PersistMoniker_SaveCompleted,
598     PersistMoniker_GetCurMoniker
599 };
600
601 /**********************************************************
602  * IMonikerProp implementation
603  */
604
605 static inline HTMLDocument *impl_from_IMonikerProp(IMonikerProp *iface)
606 {
607     return CONTAINING_RECORD(iface, HTMLDocument, IMonikerProp_iface);
608 }
609
610 static HRESULT WINAPI MonikerProp_QueryInterface(IMonikerProp *iface, REFIID riid, void **ppv)
611 {
612     HTMLDocument *This = impl_from_IMonikerProp(iface);
613     return htmldoc_query_interface(This, riid, ppv);
614 }
615
616 static ULONG WINAPI MonikerProp_AddRef(IMonikerProp *iface)
617 {
618     HTMLDocument *This = impl_from_IMonikerProp(iface);
619     return htmldoc_addref(This);
620 }
621
622 static ULONG WINAPI MonikerProp_Release(IMonikerProp *iface)
623 {
624     HTMLDocument *This = impl_from_IMonikerProp(iface);
625     return htmldoc_release(This);
626 }
627
628 static HRESULT WINAPI MonikerProp_PutProperty(IMonikerProp *iface, MONIKERPROPERTY mkp, LPCWSTR val)
629 {
630     HTMLDocument *This = impl_from_IMonikerProp(iface);
631
632     TRACE("(%p)->(%d %s)\n", This, mkp, debugstr_w(val));
633
634     switch(mkp) {
635     case MIMETYPEPROP:
636         heap_free(This->doc_obj->mime);
637         This->doc_obj->mime = heap_strdupW(val);
638         break;
639
640     case CLASSIDPROP:
641         break;
642
643     default:
644         FIXME("mkp %d\n", mkp);
645         return E_NOTIMPL;
646     }
647
648     return S_OK;
649 }
650
651 static const IMonikerPropVtbl MonikerPropVtbl = {
652     MonikerProp_QueryInterface,
653     MonikerProp_AddRef,
654     MonikerProp_Release,
655     MonikerProp_PutProperty
656 };
657
658 /**********************************************************
659  * IPersistFile implementation
660  */
661
662 static inline HTMLDocument *impl_from_IPersistFile(IPersistFile *iface)
663 {
664     return CONTAINING_RECORD(iface, HTMLDocument, IPersistFile_iface);
665 }
666
667 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
668 {
669     HTMLDocument *This = impl_from_IPersistFile(iface);
670     return htmldoc_query_interface(This, riid, ppv);
671 }
672
673 static ULONG WINAPI PersistFile_AddRef(IPersistFile *iface)
674 {
675     HTMLDocument *This = impl_from_IPersistFile(iface);
676     return htmldoc_addref(This);
677 }
678
679 static ULONG WINAPI PersistFile_Release(IPersistFile *iface)
680 {
681     HTMLDocument *This = impl_from_IPersistFile(iface);
682     return htmldoc_release(This);
683 }
684
685 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *iface, CLSID *pClassID)
686 {
687     HTMLDocument *This = impl_from_IPersistFile(iface);
688
689     TRACE("(%p)->(%p)\n", This, pClassID);
690
691     if(!pClassID)
692         return E_INVALIDARG;
693
694     *pClassID = CLSID_HTMLDocument;
695     return S_OK;
696 }
697
698 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *iface)
699 {
700     HTMLDocument *This = impl_from_IPersistFile(iface);
701
702     TRACE("(%p)\n", This);
703
704     return IPersistStreamInit_IsDirty(&This->IPersistStreamInit_iface);
705 }
706
707 static HRESULT WINAPI PersistFile_Load(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
708 {
709     HTMLDocument *This = impl_from_IPersistFile(iface);
710     FIXME("(%p)->(%s %08x)\n", This, debugstr_w(pszFileName), dwMode);
711     return E_NOTIMPL;
712 }
713
714 static HRESULT WINAPI PersistFile_Save(IPersistFile *iface, LPCOLESTR pszFileName, BOOL fRemember)
715 {
716     HTMLDocument *This = impl_from_IPersistFile(iface);
717     char *str;
718     DWORD written=0;
719     HANDLE file;
720     HRESULT hres;
721
722     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pszFileName), fRemember);
723
724     file = CreateFileW(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
725                        FILE_ATTRIBUTE_NORMAL, NULL);
726     if(file == INVALID_HANDLE_VALUE) {
727         WARN("Could not create file: %u\n", GetLastError());
728         return E_FAIL;
729     }
730
731     hres = get_doc_string(This->doc_node, &str);
732     if(SUCCEEDED(hres))
733         WriteFile(file, str, strlen(str), &written, NULL);
734
735     CloseHandle(file);
736     return hres;
737 }
738
739 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
740 {
741     HTMLDocument *This = impl_from_IPersistFile(iface);
742     FIXME("(%p)->(%s)\n", This, debugstr_w(pszFileName));
743     return E_NOTIMPL;
744 }
745
746 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *iface, LPOLESTR *pszFileName)
747 {
748     HTMLDocument *This = impl_from_IPersistFile(iface);
749     FIXME("(%p)->(%p)\n", This, pszFileName);
750     return E_NOTIMPL;
751 }
752
753 static const IPersistFileVtbl PersistFileVtbl = {
754     PersistFile_QueryInterface,
755     PersistFile_AddRef,
756     PersistFile_Release,
757     PersistFile_GetClassID,
758     PersistFile_IsDirty,
759     PersistFile_Load,
760     PersistFile_Save,
761     PersistFile_SaveCompleted,
762     PersistFile_GetCurFile
763 };
764
765 static inline HTMLDocument *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
766 {
767     return CONTAINING_RECORD(iface, HTMLDocument, IPersistStreamInit_iface);
768 }
769
770 static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface,
771                                                        REFIID riid, void **ppv)
772 {
773     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
774     return htmldoc_query_interface(This, riid, ppv);
775 }
776
777 static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
778 {
779     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
780     return htmldoc_addref(This);
781 }
782
783 static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
784 {
785     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
786     return htmldoc_release(This);
787 }
788
789 static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *pClassID)
790 {
791     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
792     return IPersistFile_GetClassID(&This->IPersistFile_iface, pClassID);
793 }
794
795 static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
796 {
797     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
798
799     TRACE("(%p)\n", This);
800
801     if(This->doc_obj->usermode == EDITMODE)
802         return editor_is_dirty(This);
803
804     return S_FALSE;
805 }
806
807 static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, LPSTREAM pStm)
808 {
809     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
810     IMoniker *mon;
811     HRESULT hres;
812
813     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
814
815     TRACE("(%p)->(%p)\n", This, pStm);
816
817     hres = CreateURLMoniker(NULL, about_blankW, &mon);
818     if(FAILED(hres)) {
819         WARN("CreateURLMoniker failed: %08x\n", hres);
820         return hres;
821     }
822
823     prepare_for_binding(This, mon, FALSE);
824     hres = set_moniker(This, mon, NULL, NULL, TRUE);
825     IMoniker_Release(mon);
826     if(FAILED(hres))
827         return hres;
828
829     return channelbsc_load_stream(This->window->pending_window, pStm);
830 }
831
832 static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, LPSTREAM pStm,
833                                              BOOL fClearDirty)
834 {
835     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
836     char *str;
837     DWORD written=0;
838     HRESULT hres;
839
840     TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
841
842     hres = get_doc_string(This->doc_node, &str);
843     if(FAILED(hres))
844         return hres;
845
846     hres = IStream_Write(pStm, str, strlen(str), &written);
847     if(FAILED(hres))
848         FIXME("Write failed: %08x\n", hres);
849
850     heap_free(str);
851
852     if(fClearDirty)
853         set_dirty(This, VARIANT_FALSE);
854
855     return S_OK;
856 }
857
858 static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface,
859                                                    ULARGE_INTEGER *pcbSize)
860 {
861     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
862     FIXME("(%p)->(%p)\n", This, pcbSize);
863     return E_NOTIMPL;
864 }
865
866 static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
867 {
868     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
869     IMoniker *mon;
870     HRESULT hres;
871
872     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
873
874     TRACE("(%p)\n", This);
875
876     hres = CreateURLMoniker(NULL, about_blankW, &mon);
877     if(FAILED(hres)) {
878         WARN("CreateURLMoniker failed: %08x\n", hres);
879         return hres;
880     }
881
882     prepare_for_binding(This, mon, FALSE);
883     hres = set_moniker(This, mon, NULL, NULL, FALSE);
884     IMoniker_Release(mon);
885     if(FAILED(hres))
886         return hres;
887
888     return channelbsc_load_stream(This->window->pending_window, NULL);
889 }
890
891 static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
892     PersistStreamInit_QueryInterface,
893     PersistStreamInit_AddRef,
894     PersistStreamInit_Release,
895     PersistStreamInit_GetClassID,
896     PersistStreamInit_IsDirty,
897     PersistStreamInit_Load,
898     PersistStreamInit_Save,
899     PersistStreamInit_GetSizeMax,
900     PersistStreamInit_InitNew
901 };
902
903 /**********************************************************
904  * IPersistHistory implementation
905  */
906
907 static inline HTMLDocument *impl_from_IPersistHistory(IPersistHistory *iface)
908 {
909     return CONTAINING_RECORD(iface, HTMLDocument, IPersistHistory_iface);
910 }
911
912 static HRESULT WINAPI PersistHistory_QueryInterface(IPersistHistory *iface, REFIID riid, void **ppv)
913 {
914     HTMLDocument *This = impl_from_IPersistHistory(iface);
915     return htmldoc_query_interface(This, riid, ppv);
916 }
917
918 static ULONG WINAPI PersistHistory_AddRef(IPersistHistory *iface)
919 {
920     HTMLDocument *This = impl_from_IPersistHistory(iface);
921     return htmldoc_addref(This);
922 }
923
924 static ULONG WINAPI PersistHistory_Release(IPersistHistory *iface)
925 {
926     HTMLDocument *This = impl_from_IPersistHistory(iface);
927     return htmldoc_release(This);
928 }
929
930 static HRESULT WINAPI PersistHistory_GetClassID(IPersistHistory *iface, CLSID *pClassID)
931 {
932     HTMLDocument *This = impl_from_IPersistHistory(iface);
933     return IPersistFile_GetClassID(&This->IPersistFile_iface, pClassID);
934 }
935
936 static HRESULT WINAPI PersistHistory_LoadHistory(IPersistHistory *iface, IStream *pStream, IBindCtx *pbc)
937 {
938     HTMLDocument *This = impl_from_IPersistHistory(iface);
939     FIXME("(%p)->(%p %p)\n", This, pStream, pbc);
940     return E_NOTIMPL;
941 }
942
943 static HRESULT WINAPI PersistHistory_SaveHistory(IPersistHistory *iface, IStream *pStream)
944 {
945     HTMLDocument *This = impl_from_IPersistHistory(iface);
946     ULONG len, written;
947     BSTR display_uri;
948     HRESULT hres;
949
950     TRACE("(%p)->(%p)\n", This, pStream);
951
952     if(!This->window || !This->window->uri) {
953         FIXME("No current URI\n");
954         return E_FAIL;
955     }
956
957     /* NOTE: The format we store is *not* compatible with native MSHTML. We currently
958      * store only URI of the page (as a length followed by a string) */
959     hres = IUri_GetDisplayUri(This->window->uri, &display_uri);
960     if(FAILED(hres))
961         return hres;
962
963     len = SysStringLen(display_uri);
964     hres = IStream_Write(pStream, &len, sizeof(len), &written);
965     if(SUCCEEDED(hres))
966         hres = IStream_Write(pStream, display_uri, len*sizeof(WCHAR), &written);
967     SysFreeString(display_uri);
968     return hres;
969 }
970
971 static HRESULT WINAPI PersistHistory_SetPositionCookie(IPersistHistory *iface, DWORD dwPositioncookie)
972 {
973     HTMLDocument *This = impl_from_IPersistHistory(iface);
974     FIXME("(%p)->(%x)\n", This, dwPositioncookie);
975     return E_NOTIMPL;
976 }
977
978 static HRESULT WINAPI PersistHistory_GetPositionCookie(IPersistHistory *iface, DWORD *pdwPositioncookie)
979 {
980     HTMLDocument *This = impl_from_IPersistHistory(iface);
981     FIXME("(%p)->(%p)\n", This, pdwPositioncookie);
982     return E_NOTIMPL;
983 }
984
985 static const IPersistHistoryVtbl PersistHistoryVtbl = {
986     PersistHistory_QueryInterface,
987     PersistHistory_AddRef,
988     PersistHistory_Release,
989     PersistHistory_GetClassID,
990     PersistHistory_LoadHistory,
991     PersistHistory_SaveHistory,
992     PersistHistory_SetPositionCookie,
993     PersistHistory_GetPositionCookie
994 };
995
996 void HTMLDocument_Persist_Init(HTMLDocument *This)
997 {
998     This->IPersistMoniker_iface.lpVtbl = &PersistMonikerVtbl;
999     This->IPersistFile_iface.lpVtbl = &PersistFileVtbl;
1000     This->IMonikerProp_iface.lpVtbl = &MonikerPropVtbl;
1001     This->IPersistStreamInit_iface.lpVtbl = &PersistStreamInitVtbl;
1002     This->IPersistHistory_iface.lpVtbl = &PersistHistoryVtbl;
1003 }