1

I hava a TWebBrowser component into I load the URL of an enriched text editor. After editing I would like to retrieve the HTML of the text (with all its markup).

Taking a look with the browser debugger I can see the editor stores the text in an iframe:

HTML Overview

I can get the iframe with this:

  NodeName := 'htmleditor_ifr';
  BodyIframe := (WebBrowser1.Document as IHTMLDocument3 ).getElementById(NodeName);

But I don't know how to retrieve the inner document.

Any tips?

Héctor C.
  • 433
  • 4
  • 17
  • [`contentDocument`](https://stackoverflow.com/questions/6581803/contentdocument-for-an-iframe) – Olivier Dec 22 '20 at 11:44
  • @Olivier I see your answer is for JavaScript, I'm working with Delphi. – Héctor C. Dec 22 '20 at 12:12
  • My answer is for DOM, which you are working with. [`getElementById()`](https://www.w3schools.com/jsref/met_document_getelementbyid.asp) is also DOM, in case you don't know. – Olivier Dec 22 '20 at 12:15
  • Delphi has the getElementById method, but not a contentDocument method so I can't use that approach, sadly. – Héctor C. Dec 22 '20 at 12:23
  • Try casting the returned element to [`IHTMLIFrameElement3 `](https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/cc288490(v=vs.85)) and then you will be able to access the `contentDocument` property. – Olivier Dec 22 '20 at 12:44
  • I'm using Delphi 7. I can find IHTMLIFrameElement or IHTMLIFrameElement2 but no IHTMLIFrameElement3 – Héctor C. Dec 22 '20 at 12:45
  • Then can you cast the element to [`IHTMLFrameBase2`](https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752410(v=vs.85)) instead and access the `contentWindow` property? – Olivier Dec 22 '20 at 13:02
  • There's neither IHTMLFrameBase2 nor IHTMLIFrameBase2 in this Delphi version. I can't find any interface with the method contentWindow either. – Héctor C. Dec 22 '20 at 15:59
  • You can try to cast it to `IUnknown` and put the result in a `variant` to use late binding to access the contentWindow property... – R. Hoek Dec 22 '20 at 22:07
  • @Olivier I just found out how to add all those definitions that I was missing. – Héctor C. Dec 23 '20 at 08:59

1 Answers1

0

This is my solution for Delphi 7.

My Delphi version didn't contain an implementation of IHTMLIFrameElement3 but the IDE offers a way to add it to your project:

Menu Component > Import ActiveX Control

Import ActiveX Control dialog

With this dialog you can generate a new unit that contains all the definitions missing from the Delphi 7 installation:

  CLASS_HTMLFrameElement: TGUID = '{3050F314-98B5-11CF-BB82-00AA00BDCE0B}';
  IID_IHTMLIFrameElement: TGUID = '{3050F315-98B5-11CF-BB82-00AA00BDCE0B}';
  IID_IHTMLIFrameElement2: TGUID = '{3050F4E6-98B5-11CF-BB82-00AA00BDCE0B}';
  IID_IHTMLIFrameElement3: TGUID = '{30510433-98B5-11CF-BB82-00AA00BDCE0B}';
  DIID_DispHTMLIFrame: TGUID = '{3050F51B-98B5-11CF-BB82-00AA00BDCE0B}';
  CLASS_HTMLIFrame: TGUID = '{3050F316-98B5-11CF-BB82-00AA00BDCE0B}';

[...]

// *********************************************************************//
// Interface: IHTMLIFrameElement3
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {30510433-98B5-11CF-BB82-00AA00BDCE0B}
// *********************************************************************//
  IHTMLIFrameElement3 = interface(IDispatch)
    ['{30510433-98B5-11CF-BB82-00AA00BDCE0B}']
    function Get_contentDocument: IDispatch; safecall;
    procedure Set_src(const p: WideString); safecall;
    function Get_src: WideString; safecall;
    procedure Set_longDesc(const p: WideString); safecall;
    function Get_longDesc: WideString; safecall;
    procedure Set_frameBorder(const p: WideString); safecall;
    function Get_frameBorder: WideString; safecall;
    property contentDocument: IDispatch read Get_contentDocument;
    property src: WideString read Get_src write Set_src;
    property longDesc: WideString read Get_longDesc write Set_longDesc;
    property frameBorder: WideString read Get_frameBorder write Set_frameBorder;
  end;

// *********************************************************************//
// DispIntf:  IHTMLIFrameElement3Disp
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {30510433-98B5-11CF-BB82-00AA00BDCE0B}
// *********************************************************************//
  IHTMLIFrameElement3Disp = dispinterface
    ['{30510433-98B5-11CF-BB82-00AA00BDCE0B}']
    property contentDocument: IDispatch readonly dispid -2147413992;
    property src: WideString dispid -2147413991;
    property longDesc: WideString dispid -2147413990;
    property frameBorder: WideString dispid -2147413989;
  end;

// *********************************************************************//
// DispIntf:  DispHTMLIFrame
// Flags:     (4112) Hidden Dispatchable
// GUID:      {3050F51B-98B5-11CF-BB82-00AA00BDCE0B}
// *********************************************************************//
  DispHTMLIFrame = dispinterface
    ['{3050F51B-98B5-11CF-BB82-00AA00BDCE0B}']
    procedure setAttribute(const strAttributeName: WideString; AttributeValue: OleVariant;
                           lFlags: Integer); dispid -2147417611;
    function getAttribute(const strAttributeName: WideString; lFlags: Integer): OleVariant; dispid -2147417610;
    function removeAttribute(const strAttributeName: WideString; lFlags: Integer): WordBool; dispid -2147417609;
    property _className: WideString dispid -2147417111;
    property id: WideString dispid -2147417110;
    property tagName: WideString readonly dispid -2147417108;

 // more

Having this I followed the tips from @Olivier:

  NodeName := 'htmleditor_ifr';
  BodyIframe := (WebBrowser1.Document as IHTMLDocument3 ).getElementById(NodeName);
  ContentHTML := (((BodyIframe as IHTMLIFrameElement3 ).contentDocument) as IHTMLDocument2 );
  Body := ContentHTML.body.innerHTML;
Héctor C.
  • 433
  • 4
  • 17