0

I am using the uHTMLEdit.pas provided in RadPHP 3 as HTML editor (based on TWebbrowser).
When I load some HTML files the program crashes. As an example, save this StackOverflow page and load it in TWebbrowser. It will crash:

Crash details:

Access violation at address 005FAF9B in module 'TestHtmlEditRad.exe'. Read of address 00000000.

Crash on line Doc.Body.SetAttribute('contentEditable', 'true', 0):

procedure THTMLEdit.EditText(const text: string);
VAR
  Doc: IHTMLDocument2;
  sl: TStringList;
  f: string;
begin
  sl := TStringList.Create;
  TRY
    sl.Text := text;
    f := gettempfile('.html');
    sl.SaveToFile(f);
    wbBrowser.Navigate(f);
    Doc := GetDocument;
    if Doc <> NIL
    then Doc.Body.SetAttribute('contentEditable', 'true', 0);  **Crash HERE**
    DeleteFile(f);
  FINALLY
    FreeAndNil(sl);
  END;
end;

It works with small (not so complex) HTML files.

My question is: Is normal for TWebBrowser to crash?


To reproduce you only need this code and the uHTMLEdit.pas (already provided with Embarcadero RadPHP).

unit FormMain;
interface
USES
  Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, uHTMLEdit;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;
var
  Form1: TForm1;

IMPLEMENTATION {$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
VAR debug: string;
begin
  debug:= stringfromfile('test.htm');  // this temporary line of code is mine, for testing. All other code is Embarcadero's
  with THTMLEditDlg.Create(application) do begin
        try
            edittext(debug);
            if ShowModal= 0 
            then debug:= getText;
        finally
            free;
        end;
    end;
end;
end.
kobik
  • 21,001
  • 4
  • 61
  • 121
Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • 3
    The AV is probably b/c either Doc or Body are not instantiated yet at the moment you call `Doc.Body.SetAttribute`. I know nothing about uHTMLEdit.pas. Try to load your file directly into a `TWebBrowser` and then set it's: `Body.SetAttribute...` on the document complete event. remember that by default the TWebBrowser uses IE7 compatibility mode. see: http://stackoverflow.com/questions/25843845/how-to-have-delphi-twebbrowser-component-running-in-ie9-mode – kobik Sep 20 '16 at 15:44
  • Also, to switch into edit mode you can also try `Doc.DesignMode := 'On'` after the document is fully loaded. – kobik Sep 20 '16 at 15:46
  • 1
    @kobik-I don't think this is the reason. The code works for some files. So, Doc is instantiated. – Gabriel Sep 20 '16 at 15:46
  • Well, test it then. e.g. `if Assigned....` for both Doc and Body. – kobik Sep 20 '16 at 15:48
  • @kobik Maybe DesignMode=on will help. I will try it. Thanks. PS: I added some code from uHTMLEdit. – Gabriel Sep 20 '16 at 15:53
  • 4
    After you edit, `wbBrowser.Navigate(f);` is **asynchronous**. You need to wait for a ReadyState of READYSTATE_COMPLETE. or use the DocumentComplete event. IMO, this code is poorly written. – kobik Sep 20 '16 at 15:55
  • @kobik - This is code from Embarcadero's HTML editor (used in RadPHP which is indeed super unstable). Tanks again. This might be the solution! – Gabriel Sep 20 '16 at 16:00
  • Except for the line "s:= stringfromfile" (which is there temporary, for easy debugging) all code is Embarcadero's. I don't know why they use a temp file. – Gabriel Sep 20 '16 at 16:13
  • @kobik - I will accept your answer "you need to wait for a ReadyState..." – Gabriel Sep 21 '16 at 08:56

1 Answers1

2

In the THTMLEdit.EditText method:

...
wbBrowser.Navigate(f);
Doc := GetDocument;
if Doc <> NIL
then Doc.Body.SetAttribute('contentEditable', 'true', 0);  **Crash HERE**
...

wbBrowser.Navigate(f) is asynchronous. When you call Doc.Body.SetAttribute either Doc or Doc.Body might not be ready/instantiated yet. this is the cause of the AV.

Since Navigate is asynchronous, You need to wait for the TWebBrowser to fully load and initialize its DOM. This is commonly done by e.g.:

  wbBrowser.Navigate(f);
  while wbBrowser.ReadyState <> READYSTATE_COMPLETE do
    Application.ProcessMessages;
  ...

Since Application.ProcessMessages considered to be "evil", and may cause re-entrance issues (unless You handle it correctly), a better approach should be to use the TWebBrowser.OnDocumentComplete event where the document/frame is fully loaded, and access the (ready/initialized) DOM there.

kobik
  • 21,001
  • 4
  • 61
  • 121