innerHTML with TAdvWebBrowser

Hello,

I am trying to extract the adress of a website where the user can move a marker.
It looks like this:

In a TAdvWebWebbrowser I use the function suggested in this forum :

AdvWebBrowser1.ExecuteJavascript('function GetHTML(){return unescape(document.documentElement.innerHTML);} GetHTML();',
procedure(const AValue: string)
begin
memo2.Lines.Text := TJSONObject.ParseJSONValue(AValue).Value;
end
);

I uses this exactg same code in 2 applications.
App1 works, App2 returns an empty string .

Any ideas what can cause this ?

kind regards,

Dirk Janssens.

Hi,

It's strange that one app is functioning properly while the other one returns an empty string even though the code i exactly the same. Is the application running on another PC? Did you check the version number of the edge chromium installed on that PC?

It is the same PC, same Delphi-version, same components, just another project.
Can it be that it is a diference in dll's in the projectflder, or maybe the uses-clause ?

Are you copying the DLLs in the output exe directory? If so, it can be that this is the reason. Please check if the DLLs are the latest version available in the installation directory of TMS VCL UI Pack.

Hi Pieter,

It seems the cause was a combination of different things.
There was a method called in an TActionUpdate-event that caused the memo to be empty again. Also I did not understand the fact that ExecuteJavascript was asynchroon.

I now have a repeat-until loop with application.processmessages in it, that repeats until the memo.lines are not empty.

QUESTION: Is there a better way to do this ?
(I would like to have a function that returns the innerHtml as a result.)

Also, what could be the equivalent of the event OnCommandStateChange of the TWebBrowser-component ? I used this to udpate the data after the marker was moved. I tried many thins (like mouseup, documentComplete etc..) but no event is triggered after I moved the marker in the map.

kind regards,

Dirk.

Hi,

In the latest version, we have added ExecuteJavaScriptSync, which synchronously returns the value from the JavaScript function, so you can actually replace the code with

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Text := TJSONObject.ParseJSONValue(AdvWebBrowser1.ExecuteJavaScriptSync('function GetHTML(){return unescape(document.documentElement.innerHTML);} GetHTML();')).Value;
end;

To capture the Marker being dragged, I suppose you can use the following code:

unit Unit22;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvCustomControl, AdvWebBrowser,
  Vcl.StdCtrls;

type
  TForm22 = class(TForm)
    AdvWebBrowser1: TAdvWebBrowser;
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form22: TForm22;

implementation

{$R *.dfm}

uses
  AdvWebBrowser.Win, AdvUtils, System.Threading;

type
  TCoreWebView2HistoryChangedEventHandler = class(TInterfacedObject, ICoreWebView2HistoryChangedEventHandler)
  public
    function Invoke(sender: ICoreWebView2; args: IUnknown): HRESULT; stdcall;
  end;

procedure TForm22.FormCreate(Sender: TObject);
var
  c: ICoreWebView2Controller;
  w: ICoreWebView2;
  e: EventRegistrationToken;
begin
  AdvWebBrowser1.URL := 'https://maps.google.com';
  c := ICoreWebView2Controller(AdvWebBrowser1.NativeBrowser);
  if c.get_CoreWebView2(w) = S_OK then
  begin
    w.add_HistoryChanged(TCoreWebView2HistoryChangedEventHandler.Create, @e);
  end;
end;

{ TCoreWebView2HistoryChangedEventHandler }

function TCoreWebView2HistoryChangedEventHandler.Invoke(sender: ICoreWebView2;
  args: IInterface): HRESULT;
var
  t: ITask;
begin
  Result := S_OK;
  t := TTask.Create(
  procedure
  begin
    TThread.Synchronize(TThread.Current,
    procedure
    begin
      TAdvUtils.Log(Form22.AdvWebBrowser1.ExecuteJavaScriptSync('window.location.href'));
    end);
  end
  );
  t.Start;
end;

end.

Thank you very much.

I installed the latest version, and now use the ExecuteJavaScriptSync method.

The other code does not compile however:

The line TAdvUtils.Log(TfrmMapSetCoord2.awb2.ExecuteJavaScriptSync('window.location.href') );

gives the error:
-> [dcc32 Error] jjjMapSetCoord2.pas(1917): E2096 Method identifier expected

FYI: awb2 is my TAdvWebbrowser on the form of type TfrmMapSetCoord2

kind regards,

Dirk Janssens.

I suppose that it should be something like:

TAdvUtils.Log(Form1.awb2.ExecuteJavaScriptSync('window.location.href') );

or at least the form declaration variable (above the implementation section). Right now, I see you are using the form class type instead of the actual variable.

Ah, I see, it compiles now.

But what is the next step ? I am not familiar with this technique, so sorry if it is a simple question.

I thought :
the code you put in the FormCreate, I placed that in the procedure where I do the navigation: each time after the URL is set. Is that correct ?

Also, If I put a breakpoint in "TCoreWebView2HistoryChangedEventHandler", that is never executed.
Where should I place my code to read the new html ( to extract the new location of the marker ) ?

You should place the code inside the FormCreate, then it should be triggered when navigating. If the history is not updated, then there is a need for another event. Can you elaborate on the technique that you are using to update the marker? Is the URL updated? Alternatively, you can start a timer, and read out the marker using JavaScript or read out the URL.

Thank you, but this does not work --> the event is not triggered when moving the marker.
Let me try to explain how it works:
When the marker is moved around, javascript functions call the google API and the GPS-location and the nearest adress is shown.
The URL is not updated.
In TwebBrowser, the event OnCommandStateChange is triggered when moving the marker around.
in the OnCommandStateChange event, I enable a timer to read the (changed) html after 100ms.
I double check the state (moving or not) , and if the marker is not moving, I extract the new adres from the html.

Additionaly : I tried to mimmick the old behaviour with TAdvWebBrowser-events like mousemove, mousedown, mouseUp... but these events are also not triggerd when I move the mouse over the map...
What is the purpuse of these events since they never get triggered ?

They are inherited from a base control. They are currently not triggered, we'll add this on our list for investigation. I'm currently looking into the marker update event question. Will update this post with a sample on how to achieve this.

:+1:

Hi,

So what you can do to communicate with the client from a web-page, is installing a bridge. This involves adding an object, that implements the IAdvCustomWebBrowserBridge. This allows you to call the object from JavaScript and send a message back to the client. The important part is:

  '    var obj = window.chrome.webview.hostObjects.sync.MyBridge;' + LB +
  '    console.log(obj);' + LB +
  '    if (obj) {' + LB +
  '      obj.ObjectMessage = "Hello World";' + LB +
  '    }' + LB +

BridgeSample.zip (6.0 KB)

unit Unit22;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvCustomControl, AdvWebBrowser,
  Vcl.StdCtrls;

type
  TMyBridgeObject = class(TInterfacedObject, IAdvCustomWebBrowserBridge)
    function GetObjectMessage: string;
    procedure SetObjectMessage(const AValue: string);
  published
    property ObjectMessage: string read GetObjectMessage write SetObjectMessage;
  end;

  TForm22 = class(TForm)
    AdvWebBrowser1: TAdvWebBrowser;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyBridge: TMyBridgeObject;
  public
    { Public declarations }
  end;

var
  Form22: TForm22;

implementation

{$R *.dfm}

uses
  AdvUtils;

procedure TForm22.FormCreate(Sender: TObject);
const
  LB = #13#10;
  MyHTML: string =
  '<!DOCTYPE html>' + LB +
  '<html>' + LB +
  '<body>' + LB +

  '<button onclick="myFunction()">Click me</button>' + LB +

  '<script>' + LB +
  '  function myFunction() {' + LB +
  '    var obj = window.chrome.webview.hostObjects.sync.MyBridge;' + LB +
  '    console.log(obj);' + LB +
  '    if (obj) {' + LB +
  '      obj.ObjectMessage = "Hello World";' + LB +
  '    }' + LB +
  '  }' + LB +
  '</script>' + LB +

  '</body>' + LB +
  '</html>';
begin
  FMyBridge := TMyBridgeObject.Create;
  AdvWebBrowser1.AddBridge('MyBridge', FMyBridge);
  AdvWebBrowser1.LoadHTML(MyHTML);
end;

{ TMyBridgeObject }

function TMyBridgeObject.GetObjectMessage: string;
begin

end;

procedure TMyBridgeObject.SetObjectMessage(const AValue: string);
begin
  TAdvUtils.Log(AValue);
end;

end.

Thank you, I will look into that. ! ( may take a while )

No worries, let me know if you need anything else :slight_smile: