Recent

Author Topic: fpCef3:ICefDomDocument - GetElementById('someid')  (Read 6131 times)

PaulANormanNZ

  • Full Member
  • ***
  • Posts: 115
fpCef3:ICefDomDocument - GetElementById('someid')
« on: January 15, 2018, 12:31:19 pm »
Hi,

My question builds upon the posting

"how to use fpCEF3 webbrowser with getElementByXxxx methods?"
http://forum.lazarus.freepascal.org/index.php/topic,34519.msg226325.html#msg226325

I want to work with elements with known html id(s)

I suppose I was expecting something more like the old MSHHTML tlb but,
I find that  Chromium.Browser.MainFrame does not expose it's contained document (ICefDomDocument)

So I am trying things like...

Quote
//global
var
Form1 : TForm;
Doc : ICefDomDocument;

...

procedure VisitDOM(const visit: ICefDomDocument);
begin
  //global
    Doc := visit;
end;

procedure TForm1.btnSetDivClick(Sender: TObject);
var

 thisNode : ICefDomNode;
begin

  // My simple document is definitely loaded, so I try ...

     Chromium.Browser.MainFrame.VisitDomProc(@VisitDOM);


     // This line always throws a  "External SigSEVEV" error ...

   thisNode := Doc.GetElementById('trialArea') ;

   showmessage(thisNode.ElementInnerText);

   // Just can't even compile the following, even though it is a property ...
   //  thisNode.ElementInnerText := 'Hello';

end;             

Any help appreciated please,

Paul
« Last Edit: January 15, 2018, 10:50:01 pm by PaulANormanNZ »

dliw

  • New Member
  • *
  • Posts: 21
Re: fpCef3:ICefDomDocument - GetElementById('someid')
« Reply #1 on: January 19, 2018, 01:20:07 pm »
Michls posts in the mentioned topic should already answer your question I think.

In short:
CEF uses many threads and multiple processes. So in general you cannot assume that a variable is valid outside of a callback. That's the reason you get the SigSEVEV.

I'll try to explain more in-depth. The pattern is always the same - so I hope this will help you to better understand CEF and fpCEF:

You have the "VisitDomProc" - it's a helper method for VisitDom
Code: Pascal  [Select][+][-]
  1. VisitDom(TCefFastDomVisitor.Create(proc) as ICefDomVisitor);
As you can see, it creates a callback object, that wraps your callback function. You could have also passed a descendant of TCefDomVisitorOwn to the VisitDom method.

That's the OOP abstraction layer of fpCEF. Now to the bottom CEF API layer - and how to find documentation:

Browser is the abstraction of TCefBrowser.
A bit of scrolling explains what MainFrame does:
Code: Pascal  [Select][+][-]
  1. // Returns the main (top-level) frame for the browser window.
  2. get_main_frame: function(self: PCefBrowser): PCefFrame; cconv;

Ok, so we get a TCefFrame.

So let's see what "VisitDom" does:
Code: Pascal  [Select][+][-]
  1. // Visit the DOM document. This function can only be called from the render
  2. // process.
  3. visit_dom: procedure(self: PCefFrame; visitor: PCefDomVisitor); cconv;

This tells us: we must be sure, that we are in the render process right now. If we are not, we need to use messages to communicate (please see the example in the referenced post).

Last step, see what TCefDomVisitor does:
Code: Pascal  [Select][+][-]
  1. // Structure to implement for visiting the DOM. The functions of this structure
  2. // will be called on the render process main thread.
and
Code: Pascal  [Select][+][-]
  1. // Method executed for visiting the DOM. The document object passed to this
  2. // function represents a snapshot of the DOM at the time this function is
  3. // executed. DOM objects are only valid for the scope of this function. Do not
  4. // keep references to or attempt to access any DOM objects outside the scope
  5. // of this function.
  6. visit: procedure(self: PCefDomVisitor; document: PCefDomDocument); cconv;
Ok, it clearly says, that references are not valid outside. So we have to process everything inside the callback or implement a callback class, that holds the needed information.
Also this callback is called in the render process - so we need messages to pass any information back to our main process.

You now may ask: But I only programmed one process / executable?
Well - see the process explorer, your application is started multiple times, having a different function each time.

For more information on this topic read:
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-processes



As you can see, CEF is an complex beast - don't use it if you don't really need a full-blown browser. Especially not for HTML parsing - there are better solutions.
« Last Edit: January 19, 2018, 01:31:37 pm by dliw »

PaulANormanNZ

  • Full Member
  • ***
  • Posts: 115
Re: fpCef3:ICefDomDocument - GetElementById('someid')
« Reply #2 on: January 21, 2018, 07:18:30 am »
Many thanks that is fantastic information.

Completely different paradigm to MSTHML_TLB really!

Quote
Last step, see what TCefDomVisitor does:

I will keep trying to find help in the appropriate part of the source code thanks.

Also the very helpful reference made in the GitHub Wiki to direct information being available ---
Quote
Some basic usage patterns are in the examples, also search for answers on the CEF forum. As fpCEF is a header translation, the usage pattern does not change, whether you use C or Pascal.

E.g. http://magpcss.org/ceforum/apidocs3/ awesome

http://magpcss.org/ceforum/index.php

Many thanks,

Paul



« Last Edit: January 21, 2018, 07:20:31 am by PaulANormanNZ »

PaulANormanNZ

  • Full Member
  • ***
  • Posts: 115
Re: fpCef3:ICefDomDocument - GetElementById('someid')
« Reply #3 on: January 22, 2018, 01:37:46 am »
The more I look into all this - the more I wish we could construct some sort of "wrapper" around it all to behave more like MSTHML_TLB and somewhat 'version' proof it all :-)

But I can't see any easy way to do that .. other than simply inventing and relying on some sort of temporary persistence (oxymoron!) on the document object perhaps?

Bit of a stab in the dark - but could the whole browser or more likely  just the document model be temporarily "cloned" and the snapshot maintained alive in memory, and project wide be stably accessed globally (until released/refreshed) using its own object model as the built in wrapper?

-- Or is that what has already been described as being impossible?

Thing is I could see that what we might call "Read" properties/functions would be pretty fine, of course the "write" type of things would require a "live" document snapshot as it were.

Meanwhile, trudge on with things as they are - with some fresh thought it is all already, entirely workable of course :-)
« Last Edit: January 22, 2018, 04:37:45 am by PaulANormanNZ »

dliw

  • New Member
  • *
  • Posts: 21
Re: fpCef3:ICefDomDocument - GetElementById('someid')
« Reply #4 on: January 22, 2018, 04:20:07 pm »
Of course you can clone the DOM and keep a copy in global memory, so that it can be accessed globally.

But this doesn't change the general situation: the DOM is in one process and your UI / Lazarus application in a different process. There is no way to access variables of a different process.

The only way to exchange information is by using IPC (wiki). And this exactly exists in CEF in the form of process messages. fpCEF has a OOP abstraction for this, so it is easy to use.

You could use process messages to transfer the whole DOM to your UI process. But usually it will be much more efficient to only transfer the information you need and do the processing in the renderer processes (these contain the DOM).

PaulANormanNZ

  • Full Member
  • ***
  • Posts: 115
Re: fpCef3:ICefDomDocument - GetElementById('someid')
« Reply #5 on: February 02, 2018, 08:41:34 am »
Thanks

 

TinyPortal © 2005-2018