TWebBrowser аварийно завершает работу с большинством HTML-файлов

Я использую uHTMLEdit.pas, предоставленный в RadPHP 3, в качестве редактора HTML (на основе TWebbrowser).
Когда я загружаю некоторые файлы HTML, программа аварийно завершает работу. В качестве примера сохраните эту страницу StackOverflow и загрузите ее в TWebbrowser. Он выйдет из строя:

Детали аварии:

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

Сбой в строке 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;

Он работает с небольшими (не такими сложными) файлами HTML.

У меня вопрос: нормально ли происходит сбой TWebBrowser?


Для воспроизведения вам понадобится только этот код и файл uHTMLEdit.pas (уже предоставленный вместе с 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.

person Z80    schedule 20.09.2016    source источник
comment
AV, вероятно, потому что либо Doc, либо Body еще не созданы экземпляры в тот момент, когда вы вызываете Doc.Body.SetAttribute. Я ничего не знаю о uHTMLEdit.pas. Попробуйте загрузить файл непосредственно в TWebBrowser, а затем установите его: Body.SetAttribute... в событии завершения документа. помните, что по умолчанию TWebBrowser использует режим совместимости с IE7. см.: stackoverflow.com/questions/25843845/   -  person kobik    schedule 20.09.2016
comment
Кроме того, чтобы переключиться в режим редактирования, вы также можете попробовать Doc.DesignMode := 'On' после полной загрузки документа.   -  person kobik    schedule 20.09.2016
comment
@kobik-я не думаю, что это причина. Код работает для некоторых файлов. Итак, Doc создан.   -  person Z80    schedule 20.09.2016
comment
Ну тогда тестируй. например if Assigned.... как для документа, так и для тела.   -  person kobik    schedule 20.09.2016
comment
@kobik Может быть, DesignMode=on поможет. Я попробую. Спасибо. PS: я добавил немного кода из uHTMLEdit.   -  person Z80    schedule 20.09.2016
comment
После редактирования wbBrowser.Navigate(f); становится асинхронным. Вам нужно дождаться ReadyState READYSTATE_COMPLETE. или используйте событие DocumentComplete. ИМО, этот код плохо написан.   -  person kobik    schedule 20.09.2016
comment
@kobik - это код HTML-редактора Embarcadero (используется в RadPHP, который действительно очень нестабилен). Снова танки. Это может быть решением!   -  person Z80    schedule 20.09.2016
comment
За исключением строки s:= stringfromfile (она временная, для облегчения отладки), весь код принадлежит Embarcadero. Я не знаю, почему они используют временный файл.   -  person Z80    schedule 20.09.2016
comment
@kobik - я приму ваш ответ, вам нужно дождаться ReadyState...   -  person Z80    schedule 21.09.2016


Ответы (1)


В методе THTMLEdit.EditText:

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

wbBrowser.Navigate(f) является асинхронным. Когда вы вызываете Doc.Body.SetAttribute, либо Doc, либо Doc.Body могут быть еще не готовы/не созданы. это причина АВ.

Поскольку Navigate является асинхронным, вам нужно дождаться полной загрузки TWebBrowser и инициализации его DOM. Это обычно делается, например:

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

Поскольку Application.ProcessMessages считается «злом» и может вызвать проблемы с повторным входом (если вы не справитесь с этим правильно), лучшим подходом должно быть использование TWebBrowser.OnDocumentComplete, когда документ/фрейм полностью загружен, и получить там доступ к (готовому/инициализированному) DOM.

person kobik    schedule 22.09.2016