cefsharp / CefSharp

.NET (WPF and Windows Forms) bindings for the Chromium Embedded Framework
http://cefsharp.github.io/
Other
9.87k stars 2.92k forks source link

How do you access web elements by Xpath? #830

Closed markrussel closed 9 years ago

markrussel commented 9 years ago

I can't seem to figure out how do create a reference to an html element, let's say an input box and then set the text programmatically.

I've tried using EvaluateScript (as per the example BrowserTabUserControl.cs) as follows. First navigate to : www,google.com page Then run:

object htmlelement = _bookmaker.WebBrowser.EvaluateScript(@"(function() { return document.evaluate(""//*[@id='gbqfq']"", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); })();");

But when I inspect htmlElement nothing has been returned.
I would really appreciate it if someone could show me how to get an element and then set the text. I'm stuck. Thanks.

jornh commented 9 years ago

Note document.evaluate will return an XPathResult object which per the note in the FAQ: https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions#CallJSWithResult isn't automagically converted over to a .NET object.

I can't seem to figure out how do create a reference to an html element, let's say an input box and then set the text programmatically.

If your use case is just to set the text programmatically, why not just provide it as part of your call to the JS side (like the color setting in FAQ entry no. 1)? Then you don't need to return anything.

markrussel commented 9 years ago

Jornh, thanks for your quick reply. The intention was to to be able to fully automate websites (embedded in a winform) using chromium web browser. I converted over to cefsharp because Microsoft's supplied webbrowser winform component was very buggy - at least I couldn't get it working properly. Also it didnt support XPath. To fully automate, I need to be able to read text, click on text and buttons, set text, loop through elements in table and send the text back to .NET to name a few. Also in some cases I may want to read an image. Do you think all these kind of things can be done in JavaScript? Unfortunately I'm not a Dot Net guru, but I am persistent :)

amaitland commented 9 years ago

CefSharp doesn't directly expose the DOM, if you wish to take control of the browser, one possibility is ChromeDriver, see https://github.com/cefsharp/CefSharp/issues/761#issuecomment-74021227

markrussel commented 9 years ago

Thanks. I suspect ChromeDriver's what I need. I'll check it out. Another learning curve :)

amaitland commented 9 years ago

Looks like there is a fairly recent nuget package

https://www.nuget.org/packages/Selenium.WebDriver.ChromeDriver/

markrussel commented 9 years ago

Thanks Alex, I'll check it out.

Regards Mark

On 23 Feb 2015, at 2:26 pm, Alex Maitland notifications@github.com wrote:

Looks like there is a fairly recent nuget package

https://www.nuget.org/packages/Selenium.WebDriver.ChromeDriver/

— Reply to this email directly or view it on GitHub.

markrussel commented 9 years ago

Ok Chaps. I'm back on this project after about a two month break. I have downloaded ChromeDriver as per your suggestion Alex. But once again I'm stuck. It seems that ChromeDriver is great for controlling Chrome but how do you point it to a ChromiumWebBrowser object embedded in a winform? I've looked at the example: https://bitbucket.org/chromiumembedded/cef/wiki/UsingChromeDriver, however seems to suggest that you must pass an executable to ChromeOptions. Now my application may have multiple embedded browsers. Therefore surely ChromeDriver needs to refer to a object ref? I'm using the BrowserTabUserControl example provided which seems fine but I want flexible control as discussed above and I can't find any examples that look close to what I'm doing.

Has anyone done this? Can it work? To reiterate I want to have: i. an embedded browser in a dot-net (at the moment winform) application ii. to be able to programmatically interact with all elements in the browser ( ChromiumWebBrowser object ). Thoughts?

amaitland commented 9 years ago

Have you though about using CefGlue? Might be a little harder to get started with, I think it supports DOM access though.

https://bitbucket.org/xilium/xilium.cefglue/wiki/Home

markrussel commented 9 years ago

Once again thanks for the suggestion. Cefglue could be a little out of my depth, especially without much doco / examples.
Obviously it'd be nice if CefSharp supported DOM access. I've read from another forum its a matter of 'wrapping the cef_dom api' but once again probably a bit hard for me - wouldn't know where to start. But if I don't find another way I guess I still have the option of trying to code lots of stuff in JavaScript and return strings back to the dot net side. Could be a little clunky, especially when I'm trying to loop through values in tables etc but as yet I don't see another way.

rassilon commented 9 years ago

ChromeDriver doesn't appear to examine much of anything else if you provide it with a Chrome debugging address via ChromeOptions: ChromeDriver Source

I would suggest trying that, you'd obviously need to have each separate ChromiumWebBrowser have a unique debugging address of course.

Bill

LeMoussel commented 9 years ago

Until CefSharp support DOM access, you can serialize node with serializeToString .

(function() {
    var domNode = document.evaluate('//a', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    return new XMLSerializer().serializeToString(domNode.singleNodeValue);
})();");
rassilon commented 9 years ago

If memory serves, CEF itself isn't too found of it's DOM support. i.e. CEF might completely remove DOM access from API

As well as this fun one memory leaks with DOMEventListeners.

CEF also is unwilling to expose the underlying WebKit DOM interfaces via CEF as well.

Thus the recommended way to access the DOM is either via JavaScript or ChromeDriver (i.e. Chromium Debug Protocol).

Fyi, Bill

amaitland commented 9 years ago

i.e. CEF might completely remove DOM access from API

Interesting find, I hadn't read that before. I know the API has some problems, wasn't aware of the deprecated status

markrussel commented 9 years ago

Ok thanks everyone. Sounds like JavaScript is the way to go. Seems like I will be able to do it all with EvaluateScript, ExecuteScript and pass objects between dotnet and java with serialization as per LeMoussel advice. I think a library of helper functions will make sense here. Mark