text: /*
   * Copyright 2005 Jacek Caban
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #include "config.h"
  
  #include <stdarg.h>
  #include <stdio.h>
  #include <assert.h>
  
  #define COBJMACROS
  
  #include "windef.h"
  #include "winbase.h"
  #include "winuser.h"
  #include "ole2.h"
  #include "shlguid.h"
  #include "shdeprecated.h"
  #include "mshtmdid.h"
  #include "idispids.h"
  
  #include "wine/debug.h"
  
  #include "mshtml_private.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
  
  #define DOCHOST_DOCCANNAVIGATE  0
  
  typedef struct {
      IEnumUnknown IEnumUnknown_iface;
      LONG ref;
  } EnumUnknown;
  
  static inline EnumUnknown *impl_from_IEnumUnknown(IEnumUnknown *iface)
  {
      return CONTAINING_RECORD(iface, EnumUnknown, IEnumUnknown_iface);
  }
  
  static HRESULT WINAPI EnumUnknown_QueryInterface(IEnumUnknown *iface, REFIID riid, void **ppv)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
  
      if(IsEqualGUID(&IID_IUnknown, riid)) {
          TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
          *ppv = &This->IEnumUnknown_iface;
      }else if(IsEqualGUID(&IID_IEnumUnknown, riid)) {
          TRACE("(%p)->(IID_IEnumUnknown %p)\n", This, ppv);
          *ppv = &This->IEnumUnknown_iface;
      }else {
          WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
          *ppv = NULL;
          return E_NOINTERFACE;
      }
  
      IUnknown_AddRef((IUnknown*)*ppv);
      return S_OK;
  }
  
  static ULONG WINAPI EnumUnknown_AddRef(IEnumUnknown *iface)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
      LONG ref = InterlockedIncrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      return ref;
  }
  
  static ULONG WINAPI EnumUnknown_Release(IEnumUnknown *iface)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
      LONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      if(!ref)
          heap_free(This);
  
      return ref;
  }
  
  static HRESULT WINAPI EnumUnknown_Next(IEnumUnknown *iface, ULONG celt, IUnknown **rgelt, ULONG *pceltFetched)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
  
      TRACE("(%p)->(%u %p %p)\n", This, celt, rgelt, pceltFetched);
  
      /* FIXME: It's not clear if we should ever return something here */
      if(pceltFetched)
          *pceltFetched = 0;
      return S_FALSE;
  }
  
  static HRESULT WINAPI EnumUnknown_Skip(IEnumUnknown *iface, ULONG celt)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
      FIXME("(%p)->(%u)\n", This, celt);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI EnumUnknown_Reset(IEnumUnknown *iface)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
      FIXME("(%p)\n", This);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI EnumUnknown_Clone(IEnumUnknown *iface, IEnumUnknown **ppenum)
  {
      EnumUnknown *This = impl_from_IEnumUnknown(iface);
      FIXME("(%p)->(%p)\n", This, ppenum);
      return E_NOTIMPL;
  }
  
  static const IEnumUnknownVtbl EnumUnknownVtbl = {
      EnumUnknown_QueryInterface,
      EnumUnknown_AddRef,
      EnumUnknown_Release,
      EnumUnknown_Next,
      EnumUnknown_Skip,
      EnumUnknown_Reset,
      EnumUnknown_Clone
  };
  
  /**********************************************************
   * IOleObject implementation
   */
  
  static inline HTMLDocument *impl_from_IOleObject(IOleObject *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IOleObject_iface);
  }
  
  static HRESULT WINAPI OleObject_QueryInterface(IOleObject *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI OleObject_AddRef(IOleObject *iface)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI OleObject_Release(IOleObject *iface)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      return htmldoc_release(This);
  }
  
  static void update_hostinfo(HTMLDocumentObj *This, DOCHOSTUIINFO *hostinfo)
  {
      nsIScrollable *scrollable;
      nsresult nsres;
  
      if(!This->nscontainer)
          return;
  
      nsres = nsIWebBrowser_QueryInterface(This->nscontainer->webbrowser, &IID_nsIScrollable, (void**)&scrollable);
      if(NS_SUCCEEDED(nsres)) {
          nsres = nsIScrollable_SetDefaultScrollbarPreferences(scrollable, ScrollOrientation_Y,
                  (hostinfo->dwFlags & DOCHOSTUIFLAG_SCROLL_NO) ? Scrollbar_Never : Scrollbar_Always);
          if(NS_FAILED(nsres))
              ERR("Could not set default Y scrollbar prefs: %08x\n", nsres);
  
          nsres = nsIScrollable_SetDefaultScrollbarPreferences(scrollable, ScrollOrientation_X,
                  hostinfo->dwFlags & DOCHOSTUIFLAG_SCROLL_NO ? Scrollbar_Never : Scrollbar_Auto);
          if(NS_FAILED(nsres))
              ERR("Could not set default X scrollbar prefs: %08x\n", nsres);
  
          nsIScrollable_Release(scrollable);
      }else {
          ERR("Could not get nsIScrollable: %08x\n", nsres);
      }
  }
  
  /* Calls undocumented 84 cmd of CGID_ShellDocView */
  void call_docview_84(HTMLDocumentObj *doc)
  {
      IOleCommandTarget *olecmd;
      VARIANT var;
      HRESULT hres;
  
      if(!doc->client)
          return;
  
      hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
      if(FAILED(hres))
          return;
  
      VariantInit(&var);
      hres = IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 84, 0, NULL, &var);
      IOleCommandTarget_Release(olecmd);
      if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
          FIXME("handle result\n");
  }
  
  void set_document_navigation(HTMLDocumentObj *doc, BOOL doc_can_navigate)
  {
      VARIANT var;
  
      if(!doc->client_cmdtrg)
          return;
  
      if(doc_can_navigate) {
          V_VT(&var) = VT_UNKNOWN;
          V_UNKNOWN(&var) = (IUnknown*)&doc->basedoc.window->base.IHTMLWindow2_iface;
      }
  
      IOleCommandTarget_Exec(doc->client_cmdtrg, &CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0,
              doc_can_navigate ? &var : NULL, NULL);
  }
  
  static void load_settings(HTMLDocumentObj *doc)
  {
      HKEY settings_key;
      DWORD val, size;
      LONG res;
  
      static const WCHAR ie_keyW[] = {
          'S','O','F','T','W','A','R','E','\\',
          'M','i','c','r','o','s','o','f','t','\\',
          'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r',0};
      static const WCHAR zoomW[] = {'Z','o','o','m',0};
      static const WCHAR zoom_factorW[] = {'Z','o','o','m','F','a','c','t','o','r',0};
  
      res = RegOpenKeyW(HKEY_CURRENT_USER, ie_keyW, &settings_key);
      if(res != ERROR_SUCCESS)
          return;
  
      size = sizeof(val);
      res = RegGetValueW(settings_key, zoomW, zoom_factorW, RRF_RT_REG_DWORD, NULL, &val, &size);
      RegCloseKey(settings_key);
      if(res == ERROR_SUCCESS)
          set_viewer_zoom(doc->nscontainer, (float)val/100000);
  }
  
  static HRESULT WINAPI OleObject_SetClientSite(IOleObject *iface, IOleClientSite *pClientSite)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      IOleCommandTarget *cmdtrg = NULL;
      IOleWindow *ole_window;
      IBrowserService *browser_service;
      BOOL hostui_setup;
      VARIANT silent;
      HWND hwnd;
      HRESULT hres;
  
      TRACE("(%p)->(%p)\n", This, pClientSite);
  
      if(pClientSite == This->doc_obj->client)
          return S_OK;
  
      if(This->doc_obj->client) {
          IOleClientSite_Release(This->doc_obj->client);
          This->doc_obj->client = NULL;
          This->doc_obj->usermode = UNKNOWN_USERMODE;
      }
  
      if(This->doc_obj->client_cmdtrg) {
          IOleCommandTarget_Release(This->doc_obj->client_cmdtrg);
          This->doc_obj->client_cmdtrg = NULL;
      }
  
      if(This->doc_obj->hostui && !This->doc_obj->custom_hostui) {
          IDocHostUIHandler_Release(This->doc_obj->hostui);
          This->doc_obj->hostui = NULL;
      }
  
      if(This->doc_obj->doc_object_service) {
          IDocObjectService_Release(This->doc_obj->doc_object_service);
          This->doc_obj->doc_object_service = NULL;
      }
  
      if(This->doc_obj->webbrowser) {
          IUnknown_Release(This->doc_obj->webbrowser);
          This->doc_obj->webbrowser = NULL;
      }
  
      if(This->doc_obj->browser_service) {
          IUnknown_Release(This->doc_obj->browser_service);
          This->doc_obj->browser_service = NULL;
      }
  
      if(This->doc_obj->travel_log) {
          ITravelLog_Release(This->doc_obj->travel_log);
          This->doc_obj->travel_log = NULL;
      }
  
      memset(&This->doc_obj->hostinfo, 0, sizeof(DOCHOSTUIINFO));
  
      if(!pClientSite)
          return S_OK;
  
      IOleClientSite_AddRef(pClientSite);
      This->doc_obj->client = pClientSite;
  
      hostui_setup = This->doc_obj->hostui_setup;
  
      if(!This->doc_obj->hostui) {
          IDocHostUIHandler *uihandler;
  
          This->doc_obj->custom_hostui = FALSE;
  
          hres = IOleClientSite_QueryInterface(pClientSite, &IID_IDocHostUIHandler, (void**)&uihandler);
          if(SUCCEEDED(hres))
              This->doc_obj->hostui = uihandler;
      }
  
      if(This->doc_obj->hostui) {
          DOCHOSTUIINFO hostinfo;
          LPOLESTR key_path = NULL, override_key_path = NULL;
          IDocHostUIHandler2 *uihandler2;
  
          memset(&hostinfo, 0, sizeof(DOCHOSTUIINFO));
          hostinfo.cbSize = sizeof(DOCHOSTUIINFO);
          hres = IDocHostUIHandler_GetHostInfo(This->doc_obj->hostui, &hostinfo);
          if(SUCCEEDED(hres)) {
              TRACE("hostinfo = {%u %08x %08x %s %s}\n",
                      hostinfo.cbSize, hostinfo.dwFlags, hostinfo.dwDoubleClick,
                      debugstr_w(hostinfo.pchHostCss), debugstr_w(hostinfo.pchHostNS));
              update_hostinfo(This->doc_obj, &hostinfo);
              This->doc_obj->hostinfo = hostinfo;
          }
  
          if(!hostui_setup) {
              hres = IDocHostUIHandler_GetOptionKeyPath(This->doc_obj->hostui, &key_path, 0);
              if(hres == S_OK && key_path) {
                  if(key_path[0]) {
                      /* FIXME: use key_path */
                      FIXME("key_path = %s\n", debugstr_w(key_path));
                  }
                  CoTaskMemFree(key_path);
              }
  
              hres = IDocHostUIHandler_QueryInterface(This->doc_obj->hostui, &IID_IDocHostUIHandler2,
                      (void**)&uihandler2);
              if(SUCCEEDED(hres)) {
                  hres = IDocHostUIHandler2_GetOverrideKeyPath(uihandler2, &override_key_path, 0);
                  if(hres == S_OK && override_key_path) {
                      if(override_key_path[0]) {
                          /*FIXME: use override_key_path */
                          FIXME("override_key_path = %s\n", debugstr_w(override_key_path));
                      }
                      CoTaskMemFree(override_key_path);
                  }
                  IDocHostUIHandler2_Release(uihandler2);
              }
  
              This->doc_obj->hostui_setup = TRUE;
          }
      }
  
      load_settings(This->doc_obj);
  
      /* Native calls here GetWindow. What is it for?
       * We don't have anything to do with it here (yet). */
      hres = IOleClientSite_QueryInterface(pClientSite, &IID_IOleWindow, (void**)&ole_window);
      if(SUCCEEDED(hres)) {
          IOleWindow_GetWindow(ole_window, &hwnd);
          IOleWindow_Release(ole_window);
      }
  
      hres = do_query_service((IUnknown*)pClientSite, &IID_IShellBrowser,
              &IID_IBrowserService, (void**)&browser_service);
      if(SUCCEEDED(hres)) {
          ITravelLog *travel_log;
  
          This->doc_obj->browser_service = (IUnknown*)browser_service;
  
          hres = IBrowserService_GetTravelLog(browser_service, &travel_log);
          if(SUCCEEDED(hres))
              This->doc_obj->travel_log = travel_log;
      }else {
          browser_service = NULL;
      }
  
      hres = IOleClientSite_QueryInterface(pClientSite, &IID_IOleCommandTarget, (void**)&cmdtrg);
      if(SUCCEEDED(hres)) {
          VARIANT var;
          OLECMD cmd = {OLECMDID_SETPROGRESSTEXT, 0};
  
          This->doc_obj->client_cmdtrg = cmdtrg;
  
          if(!hostui_setup) {
              IDocObjectService *doc_object_service;
              IWebBrowser2 *wb;
  
              set_document_navigation(This->doc_obj, TRUE);
  
              if(browser_service) {
                  hres = IBrowserService_QueryInterface(browser_service,
                          &IID_IDocObjectService, (void**)&doc_object_service);
                  if(SUCCEEDED(hres)) {
                      This->doc_obj->doc_object_service = doc_object_service;
  
                      /*
                       * Some embedding routines, esp. in regards to use of IDocObjectService, differ if
                       * embedder supports IWebBrowserApp.
                       */
                      hres = do_query_service((IUnknown*)pClientSite, &IID_IWebBrowserApp, &IID_IWebBrowser2, (void**)&wb);
                      if(SUCCEEDED(hres))
                          This->doc_obj->webbrowser = (IUnknown*)wb;
                  }
              }
          }
  
          call_docview_84(This->doc_obj);
  
          IOleCommandTarget_QueryStatus(cmdtrg, NULL, 1, &cmd, NULL);
  
          V_VT(&var) = VT_I4;
          V_I4(&var) = 0;
          IOleCommandTarget_Exec(cmdtrg, NULL, OLECMDID_SETPROGRESSMAX,
                  OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL);
          IOleCommandTarget_Exec(cmdtrg, NULL, OLECMDID_SETPROGRESSPOS, 
                  OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL);
      }
  
      if(This->doc_obj->usermode == UNKNOWN_USERMODE)
          IOleControl_OnAmbientPropertyChange(&This->IOleControl_iface, DISPID_AMBIENT_USERMODE);
  
      IOleControl_OnAmbientPropertyChange(&This->IOleControl_iface,
              DISPID_AMBIENT_OFFLINEIFNOTCONNECTED);
  
      hres = get_client_disp_property(This->doc_obj->client, DISPID_AMBIENT_SILENT, &silent);
      if(SUCCEEDED(hres)) {
          if(V_VT(&silent) != VT_BOOL)
              WARN("silent = %s\n", debugstr_variant(&silent));
          else if(V_BOOL(&silent))
              FIXME("silent == true\n");
      }
  
      IOleControl_OnAmbientPropertyChange(&This->IOleControl_iface, DISPID_AMBIENT_USERAGENT);
      IOleControl_OnAmbientPropertyChange(&This->IOleControl_iface, DISPID_AMBIENT_PALETTE);
  
      return S_OK;
  }
  
  static HRESULT WINAPI OleObject_GetClientSite(IOleObject *iface, IOleClientSite **ppClientSite)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
  
      TRACE("(%p)->(%p)\n", This, ppClientSite);
  
      if(!ppClientSite)
          return E_INVALIDARG;
  
      if(This->doc_obj->client)
          IOleClientSite_AddRef(This->doc_obj->client);
      *ppClientSite = This->doc_obj->client;
  
      return S_OK;
  }
  
  static HRESULT WINAPI OleObject_SetHostNames(IOleObject *iface, LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%s %s)\n", This, debugstr_w(szContainerApp), debugstr_w(szContainerObj));
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_Close(IOleObject *iface, DWORD dwSaveOption)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
  
      TRACE("(%p)->(%08x)\n", This, dwSaveOption);
  
      if(dwSaveOption == OLECLOSE_PROMPTSAVE)
          FIXME("OLECLOSE_PROMPTSAVE not implemented\n");
  
      if(This->doc_obj->in_place_active)
          IOleInPlaceObjectWindowless_InPlaceDeactivate(&This->IOleInPlaceObjectWindowless_iface);
  
      HTMLDocument_LockContainer(This->doc_obj, FALSE);
  
      if(This->advise_holder)
          IOleAdviseHolder_SendOnClose(This->advise_holder);
      
      return S_OK;
  }
  
  static HRESULT WINAPI OleObject_SetMoniker(IOleObject *iface, DWORD dwWhichMoniker, IMoniker *pmk)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p %d %p)->()\n", This, dwWhichMoniker, pmk);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_GetMoniker(IOleObject *iface, DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %d %p)\n", This, dwAssign, dwWhichMoniker, ppmk);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_InitFromData(IOleObject *iface, IDataObject *pDataObject, BOOL fCreation,
                                          DWORD dwReserved)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%p %x %d)\n", This, pDataObject, fCreation, dwReserved);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_GetClipboardData(IOleObject *iface, DWORD dwReserved, IDataObject **ppDataObject)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %p)\n", This, dwReserved, ppDataObject);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_DoVerb(IOleObject *iface, LONG iVerb, LPMSG lpmsg, IOleClientSite *pActiveSite,
                                          LONG lindex, HWND hwndParent, LPCRECT lprcPosRect)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      IOleDocumentSite *pDocSite;
      HRESULT hres;
  
      TRACE("(%p)->(%d %p %p %d %p %p)\n", This, iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect);
  
      if(iVerb != OLEIVERB_SHOW && iVerb != OLEIVERB_UIACTIVATE && iVerb != OLEIVERB_INPLACEACTIVATE) { 
          FIXME("iVerb = %d not supported\n", iVerb);
          return E_NOTIMPL;
      }
  
      if(!pActiveSite)
          pActiveSite = This->doc_obj->client;
  
      hres = IOleClientSite_QueryInterface(pActiveSite, &IID_IOleDocumentSite, (void**)&pDocSite);
      if(SUCCEEDED(hres)) {
          HTMLDocument_LockContainer(This->doc_obj, TRUE);
  
          /* FIXME: Create new IOleDocumentView. See CreateView for more info. */
          hres = IOleDocumentSite_ActivateMe(pDocSite, &This->IOleDocumentView_iface);
          IOleDocumentSite_Release(pDocSite);
      }else {
          hres = IOleDocumentView_UIActivate(&This->IOleDocumentView_iface, TRUE);
          if(SUCCEEDED(hres)) {
              if(lprcPosRect) {
                  RECT rect; /* We need to pass rect as not const pointer */
                  rect = *lprcPosRect;
                  IOleDocumentView_SetRect(&This->IOleDocumentView_iface, &rect);
              }
              IOleDocumentView_Show(&This->IOleDocumentView_iface, TRUE);
          }
      }
  
      return hres;
  }
  
  static HRESULT WINAPI OleObject_EnumVerbs(IOleObject *iface, IEnumOLEVERB **ppEnumOleVerb)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%p)\n", This, ppEnumOleVerb);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_Update(IOleObject *iface)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)\n", This);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_IsUpToDate(IOleObject *iface)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)\n", This);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_GetUserClassID(IOleObject *iface, CLSID *pClsid)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
  
      TRACE("(%p)->(%p)\n", This, pClsid);
  
      if(!pClsid)
          return E_INVALIDARG;
  
      *pClsid = CLSID_HTMLDocument;
      return S_OK;
  }
  
  static HRESULT WINAPI OleObject_GetUserType(IOleObject *iface, DWORD dwFormOfType, LPOLESTR *pszUserType)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %p)\n", This, dwFormOfType, pszUserType);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_SetExtent(IOleObject *iface, DWORD dwDrawAspect, SIZEL *psizel)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %p)\n", This, dwDrawAspect, psizel);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_GetExtent(IOleObject *iface, DWORD dwDrawAspect, SIZEL *psizel)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %p)\n", This, dwDrawAspect, psizel);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_Advise(IOleObject *iface, IAdviseSink *pAdvSink, DWORD *pdwConnection)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      TRACE("(%p)->(%p %p)\n", This, pAdvSink, pdwConnection);
  
      if(!pdwConnection)
          return E_INVALIDARG;
  
      if(!pAdvSink) {
          *pdwConnection = 0;
          return E_INVALIDARG;
      }
  
      if(!This->advise_holder) {
          CreateOleAdviseHolder(&This->advise_holder);
          if(!This->advise_holder)
              return E_OUTOFMEMORY;
      }
  
      return IOleAdviseHolder_Advise(This->advise_holder, pAdvSink, pdwConnection);
  }
  
  static HRESULT WINAPI OleObject_Unadvise(IOleObject *iface, DWORD dwConnection)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      TRACE("(%p)->(%d)\n", This, dwConnection);
  
      if(!This->advise_holder)
          return OLE_E_NOCONNECTION;
  
      return IOleAdviseHolder_Unadvise(This->advise_holder, dwConnection);
  }
  
  static HRESULT WINAPI OleObject_EnumAdvise(IOleObject *iface, IEnumSTATDATA **ppenumAdvise)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
  
      if(!This->advise_holder) {
          *ppenumAdvise = NULL;
          return S_OK;
      }
  
      return IOleAdviseHolder_EnumAdvise(This->advise_holder, ppenumAdvise);
  }
  
  static HRESULT WINAPI OleObject_GetMiscStatus(IOleObject *iface, DWORD dwAspect, DWORD *pdwStatus)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%d %p)\n", This, dwAspect, pdwStatus);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleObject_SetColorScheme(IOleObject *iface, LOGPALETTE *pLogpal)
  {
      HTMLDocument *This = impl_from_IOleObject(iface);
      FIXME("(%p)->(%p)\n", This, pLogpal);
      return E_NOTIMPL;
  }
  
  static const IOleObjectVtbl OleObjectVtbl = {
      OleObject_QueryInterface,
      OleObject_AddRef,
      OleObject_Release,
      OleObject_SetClientSite,
      OleObject_GetClientSite,
      OleObject_SetHostNames,
      OleObject_Close,
      OleObject_SetMoniker,
      OleObject_GetMoniker,
      OleObject_InitFromData,
      OleObject_GetClipboardData,
      OleObject_DoVerb,
      OleObject_EnumVerbs,
      OleObject_Update,
      OleObject_IsUpToDate,
      OleObject_GetUserClassID,
      OleObject_GetUserType,
      OleObject_SetExtent,
      OleObject_GetExtent,
      OleObject_Advise,
      OleObject_Unadvise,
      OleObject_EnumAdvise,
      OleObject_GetMiscStatus,
      OleObject_SetColorScheme
  };
  
  /**********************************************************
   * IOleDocument implementation
   */
  
  static inline HTMLDocument *impl_from_IOleDocument(IOleDocument *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IOleDocument_iface);
  }
  
  static HRESULT WINAPI OleDocument_QueryInterface(IOleDocument *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI OleDocument_AddRef(IOleDocument *iface)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI OleDocument_Release(IOleDocument *iface)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      return htmldoc_release(This);
  }
  
  static HRESULT WINAPI OleDocument_CreateView(IOleDocument *iface, IOleInPlaceSite *pIPSite, IStream *pstm,
                                     DWORD dwReserved, IOleDocumentView **ppView)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      HRESULT hres;
  
      TRACE("(%p)->(%p %p %d %p)\n", This, pIPSite, pstm, dwReserved, ppView);
  
      if(!ppView)
          return E_INVALIDARG;
  
      /* FIXME:
       * Windows implementation creates new IOleDocumentView when function is called for the
       * first time and returns E_FAIL when it is called for the second time, but it doesn't matter
       * if the application uses returned interfaces, passed to ActivateMe or returned by
       * QueryInterface, so there is no reason to create new interface. This needs more testing.
       */
  
      if(pIPSite) {
          hres = IOleDocumentView_SetInPlaceSite(&This->IOleDocumentView_iface, pIPSite);
          if(FAILED(hres))
              return hres;
      }
  
      if(pstm)
          FIXME("pstm is not supported\n");
  
      IOleDocumentView_AddRef(&This->IOleDocumentView_iface);
      *ppView = &This->IOleDocumentView_iface;
      return S_OK;
  }
  
  static HRESULT WINAPI OleDocument_GetDocMiscStatus(IOleDocument *iface, DWORD *pdwStatus)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      FIXME("(%p)->(%p)\n", This, pdwStatus);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleDocument_EnumViews(IOleDocument *iface, IEnumOleDocumentViews **ppEnum,
                                     IOleDocumentView **ppView)
  {
      HTMLDocument *This = impl_from_IOleDocument(iface);
      FIXME("(%p)->(%p %p)\n", This, ppEnum, ppView);
      return E_NOTIMPL;
  }
  
  static const IOleDocumentVtbl OleDocumentVtbl = {
      OleDocument_QueryInterface,
      OleDocument_AddRef,
      OleDocument_Release,
      OleDocument_CreateView,
      OleDocument_GetDocMiscStatus,
      OleDocument_EnumViews
  };
  
  /**********************************************************
   * IOleControl implementation
   */
  
  static inline HTMLDocument *impl_from_IOleControl(IOleControl *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IOleControl_iface);
  }
  
  static HRESULT WINAPI OleControl_QueryInterface(IOleControl *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI OleControl_AddRef(IOleControl *iface)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI OleControl_Release(IOleControl *iface)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      return htmldoc_release(This);
  }
  
  static HRESULT WINAPI OleControl_GetControlInfo(IOleControl *iface, CONTROLINFO *pCI)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      FIXME("(%p)->(%p)\n", This, pCI);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleControl_OnMnemonic(IOleControl *iface, MSG *pMsg)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      FIXME("(%p)->(%p)\n", This, pMsg);
      return E_NOTIMPL;
  }
  
  HRESULT get_client_disp_property(IOleClientSite *client, DISPID dispid, VARIANT *res)
  {
      IDispatch *disp = NULL;
      DISPPARAMS dispparams = {NULL, 0};
      UINT err;
      HRESULT hres;
  
      hres = IOleClientSite_QueryInterface(client, &IID_IDispatch, (void**)&disp);
      if(FAILED(hres)) {
          TRACE("Could not get IDispatch\n");
          return hres;
      }
  
      VariantInit(res);
  
      hres = IDispatch_Invoke(disp, dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT,
              DISPATCH_PROPERTYGET, &dispparams, res, NULL, &err);
  
      IDispatch_Release(disp);
  
      return hres;
  }
  
  static HRESULT on_change_dlcontrol(HTMLDocument *This)
  {
      VARIANT res;
      HRESULT hres;
      
      hres = get_client_disp_property(This->doc_obj->client, DISPID_AMBIENT_DLCONTROL, &res);
      if(SUCCEEDED(hres))
          FIXME("unsupported dlcontrol %08x\n", V_I4(&res));
  
      return S_OK;
  }
  
  static HRESULT WINAPI OleControl_OnAmbientPropertyChange(IOleControl *iface, DISPID dispID)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      IOleClientSite *client;
      VARIANT res;
      HRESULT hres;
  
      client = This->doc_obj->client;
      if(!client) {
          TRACE("client = NULL\n");
          return S_OK;
      }
  
      switch(dispID) {
      case DISPID_AMBIENT_USERMODE:
          TRACE("(%p)->(DISPID_AMBIENT_USERMODE)\n", This);
          hres = get_client_disp_property(client, DISPID_AMBIENT_USERMODE, &res);
          if(FAILED(hres))
              return S_OK;
  
          if(V_VT(&res) == VT_BOOL) {
              if(V_BOOL(&res)) {
                  This->doc_obj->usermode = BROWSEMODE;
              }else {
                  FIXME("edit mode is not supported\n");
                  This->doc_obj->usermode = EDITMODE;
              }
          }else {
              FIXME("usermode=%s\n", debugstr_variant(&res));
          }
          return S_OK;
      case DISPID_AMBIENT_DLCONTROL:
          TRACE("(%p)->(DISPID_AMBIENT_DLCONTROL)\n", This);
          return on_change_dlcontrol(This);
      case DISPID_AMBIENT_OFFLINEIFNOTCONNECTED:
          TRACE("(%p)->(DISPID_AMBIENT_OFFLINEIFNOTCONNECTED)\n", This);
          on_change_dlcontrol(This);
          hres = get_client_disp_property(client, DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &res);
          if(FAILED(hres))
              return S_OK;
  
          if(V_VT(&res) == VT_BOOL) {
              if(V_BOOL(&res)) {
                  FIXME("offline connection is not supported\n");
                  hres = E_FAIL;
              }
          }else {
              FIXME("offlineconnected=%s\n", debugstr_variant(&res));
          }
          return S_OK;
      case DISPID_AMBIENT_SILENT:
          TRACE("(%p)->(DISPID_AMBIENT_SILENT)\n", This);
          on_change_dlcontrol(This);
          hres = get_client_disp_property(client, DISPID_AMBIENT_SILENT, &res);
          if(FAILED(hres))
              return S_OK;
  
          if(V_VT(&res) == VT_BOOL) {
              if(V_BOOL(&res)) {
                  FIXME("silent mode is not supported\n");
                  hres = E_FAIL;
              }
          }else {
              FIXME("silent=%s\n", debugstr_variant(&res));
          }
          return S_OK;
      case DISPID_AMBIENT_USERAGENT:
          TRACE("(%p)->(DISPID_AMBIENT_USERAGENT)\n", This);
          hres = get_client_disp_property(client, DISPID_AMBIENT_USERAGENT, &res);
          if(FAILED(hres))
              return S_OK;
  
          FIXME("not supported AMBIENT_USERAGENT\n");
          hres = E_FAIL;
          return S_OK;
      case DISPID_AMBIENT_PALETTE:
          TRACE("(%p)->(DISPID_AMBIENT_PALETTE)\n", This);
          hres = get_client_disp_property(client, DISPID_AMBIENT_PALETTE, &res);
          if(FAILED(hres))
              return S_OK;
  
          FIXME("not supported AMBIENT_PALETTE\n");
          hres = E_FAIL;
          return S_OK;
      }
  
      FIXME("(%p) unsupported dispID=%d\n", This, dispID);
      return E_FAIL;
  }
  
  static HRESULT WINAPI OleControl_FreezeEvents(IOleControl *iface, BOOL bFreeze)
  {
      HTMLDocument *This = impl_from_IOleControl(iface);
      FIXME("(%p)->(%x)\n", This, bFreeze);
      return E_NOTIMPL;
  }
  
  static const IOleControlVtbl OleControlVtbl = {
      OleControl_QueryInterface,
      OleControl_AddRef,
      OleControl_Release,
      OleControl_GetControlInfo,
      OleControl_OnMnemonic,
      OleControl_OnAmbientPropertyChange,
      OleControl_FreezeEvents
  };
  
  /**********************************************************
   * IObjectWithSite implementation
   */
  
  static inline HTMLDocument *impl_from_IObjectWithSite(IObjectWithSite *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IObjectWithSite_iface);
  }
  
  static HRESULT WINAPI ObjectWithSite_QueryInterface(IObjectWithSite *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IObjectWithSite(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI ObjectWithSite_AddRef(IObjectWithSite *iface)
  {
      HTMLDocument *This = impl_from_IObjectWithSite(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI ObjectWithSite_Release(IObjectWithSite *iface)
  {
      HTMLDocument *This = impl_from_IObjectWithSite(iface);
      return htmldoc_release(This);
  }
  
  static HRESULT WINAPI ObjectWithSite_SetSite(IObjectWithSite *iface, IUnknown *pUnkSite)
  {
      HTMLDocument *This = impl_from_IObjectWithSite(iface);
      FIXME("(%p)->(%p)\n", This, pUnkSite);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI ObjectWithSite_GetSite(IObjectWithSite* iface, REFIID riid, PVOID *ppvSite)
  {
      HTMLDocument *This = impl_from_IObjectWithSite(iface);
      FIXME("(%p)->(%p)\n", This, ppvSite);
      return E_NOTIMPL;
  }
  
  static const IObjectWithSiteVtbl ObjectWithSiteVtbl = {
      ObjectWithSite_QueryInterface,
      ObjectWithSite_AddRef,
      ObjectWithSite_Release,
      ObjectWithSite_SetSite,
      ObjectWithSite_GetSite
  };
  
  /**********************************************************
   * IOleContainer implementation
   */
  
  static inline HTMLDocument *impl_from_IOleContainer(IOleContainer *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IOleContainer_iface);
  }
  
  static HRESULT WINAPI OleContainer_QueryInterface(IOleContainer *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI OleContainer_AddRef(IOleContainer *iface)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI OleContainer_Release(IOleContainer *iface)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      return htmldoc_release(This);
  }
  
  static HRESULT WINAPI OleContainer_ParseDisplayName(IOleContainer *iface, IBindCtx *pbc, LPOLESTR pszDisplayName,
          ULONG *pchEaten, IMoniker **ppmkOut)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      FIXME("(%p)->(%p %s %p %p)\n", This, pbc, debugstr_w(pszDisplayName), pchEaten, ppmkOut);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI OleContainer_EnumObjects(IOleContainer *iface, DWORD grfFlags, IEnumUnknown **ppenum)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      EnumUnknown *ret;
  
      TRACE("(%p)->(%x %p)\n", This, grfFlags, ppenum);
  
      ret = heap_alloc(sizeof(*ret));
      if(!ret)
          return E_OUTOFMEMORY;
  
      ret->IEnumUnknown_iface.lpVtbl = &EnumUnknownVtbl;
      ret->ref = 1;
  
      *ppenum = &ret->IEnumUnknown_iface;
      return S_OK;
  }
  
  static HRESULT WINAPI OleContainer_LockContainer(IOleContainer *iface, BOOL fLock)
  {
      HTMLDocument *This = impl_from_IOleContainer(iface);
      FIXME("(%p)->(%x)\n", This, fLock);
      return E_NOTIMPL;
  }
  
  static const IOleContainerVtbl OleContainerVtbl = {
      OleContainer_QueryInterface,
      OleContainer_AddRef,
      OleContainer_Release,
      OleContainer_ParseDisplayName,
      OleContainer_EnumObjects,
      OleContainer_LockContainer
  };
  
  static inline HTMLDocumentObj *impl_from_ITargetContainer(ITargetContainer *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocumentObj, ITargetContainer_iface);
  }
  
  static HRESULT WINAPI TargetContainer_QueryInterface(ITargetContainer *iface, REFIID riid, void **ppv)
  {
      HTMLDocumentObj *This = impl_from_ITargetContainer(iface);
      return ICustomDoc_QueryInterface(&This->ICustomDoc_iface, riid, ppv);
  }
  
  static ULONG WINAPI TargetContainer_AddRef(ITargetContainer *iface)
  {
      HTMLDocumentObj *This = impl_from_ITargetContainer(iface);
      return ICustomDoc_AddRef(&This->ICustomDoc_iface);
  }
  
  static ULONG WINAPI TargetContainer_Release(ITargetContainer *iface)
  {
      HTMLDocumentObj *This = impl_from_ITargetContainer(iface);
      return ICustomDoc_Release(&This->ICustomDoc_iface);
  }
  
  static HRESULT WINAPI TargetContainer_GetFrameUrl(ITargetContainer *iface, LPWSTR *ppszFrameSrc)
  {
      HTMLDocumentObj *This = impl_from_ITargetContainer(iface);
      FIXME("(%p)->(%p)\n", This, ppszFrameSrc);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI TargetContainer_GetFramesContainer(ITargetContainer *iface, IOleContainer **ppContainer)
  {
      HTMLDocumentObj *This = impl_from_ITargetContainer(iface);
  
      TRACE("(%p)->(%p)\n", This, ppContainer);
  
      /* NOTE: we should return wrapped interface here */
      IOleContainer_AddRef(&This->basedoc.IOleContainer_iface);
      *ppContainer = &This->basedoc.IOleContainer_iface;
      return S_OK;
  }
  
  static const ITargetContainerVtbl TargetContainerVtbl = {
      TargetContainer_QueryInterface,
      TargetContainer_AddRef,
      TargetContainer_Release,
      TargetContainer_GetFrameUrl,
      TargetContainer_GetFramesContainer
  };
  
  void TargetContainer_Init(HTMLDocumentObj *This)
  {
      This->ITargetContainer_iface.lpVtbl = &TargetContainerVtbl;
  }
  
  /**********************************************************
   * IObjectSafety implementation
   */
  
  static inline HTMLDocument *impl_from_IObjectSafety(IObjectSafety *iface)
  {
      return CONTAINING_RECORD(iface, HTMLDocument, IObjectSafety_iface);
  }
  
  static HRESULT WINAPI ObjectSafety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
  {
      HTMLDocument *This = impl_from_IObjectSafety(iface);
      return htmldoc_query_interface(This, riid, ppv);
  }
  
  static ULONG WINAPI ObjectSafety_AddRef(IObjectSafety *iface)
  {
      HTMLDocument *This = impl_from_IObjectSafety(iface);
      return htmldoc_addref(This);
  }
  
  static ULONG WINAPI ObjectSafety_Release(IObjectSafety *iface)
  {
      HTMLDocument *This = impl_from_IObjectSafety(iface);
      return htmldoc_release(This);
  }
  
  static HRESULT WINAPI ObjectSafety_GetInterfaceSafetyOptions(IObjectSafety *iface,
          REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
  {
      HTMLDocument *This = impl_from_IObjectSafety(iface);
      FIXME("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), pdwSupportedOptions, pdwEnabledOptions);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI ObjectSafety_SetInterfaceSafetyOptions(IObjectSafety *iface,
          REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
  {
      HTMLDocument *This = impl_from_IObjectSafety(iface);
      FIXME("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), dwOptionSetMask, dwEnabledOptions);
  
      if(IsEqualGUID(&IID_IPersistMoniker, riid) &&
              dwOptionSetMask==INTERFACESAFE_FOR_UNTRUSTED_DATA &&
              dwEnabledOptions==INTERFACESAFE_FOR_UNTRUSTED_DATA)
          return S_OK;
  
      return E_NOTIMPL;
  }
  
  static const IObjectSafetyVtbl ObjectSafetyVtbl = {
      ObjectSafety_QueryInterface,
      ObjectSafety_AddRef,
      ObjectSafety_Release,
      ObjectSafety_GetInterfaceSafetyOptions,
      ObjectSafety_SetInterfaceSafetyOptions
  };
  
  void HTMLDocument_LockContainer(HTMLDocumentObj *This, BOOL fLock)
  {
      IOleContainer *container;
      HRESULT hres;
  
      if(!This->client || This->container_locked == fLock)
          return;
  
      hres = IOleClientSite_GetContainer(This->client, &container);
      if(SUCCEEDED(hres)) {
          IOleContainer_LockContainer(container, fLock);
          This->container_locked = fLock;
          IOleContainer_Release(container);
      }
  }
  
  void HTMLDocument_OleObj_Init(HTMLDocument *This)
  {
      This->IOleObject_iface.lpVtbl = &OleObjectVtbl;
      This->IOleDocument_iface.lpVtbl = &OleDocumentVtbl;
      This->IOleControl_iface.lpVtbl = &OleControlVtbl;
      This->IObjectWithSite_iface.lpVtbl = &ObjectWithSiteVtbl;
      This->IOleContainer_iface.lpVtbl = &OleContainerVtbl;
      This->IObjectSafety_iface.lpVtbl = &ObjectSafetyVtbl;
  }
  