0

Just got started with CefSharp, and I searched for hours about how to manipulate the DOM. All the information I was able to find is kind of outdated, and stating that the only way to do DOM Manipulation is through JavaScript injection (with methods like ExecuteJavaScriptAsync and EvaluateScriptAsync).

The most recent info is more than 2 year old: Any reason to prefer CefSharp over CefGlue (or vice-versa)?
Another one from official github page (even older): https://github.com/cefsharp/CefSharp/issues/1587

But looking at the CEF source code, we can see that there is implemented some perfectly suitable methods to do exactly that, like:

virtual CefRefPtr<CefDOMNode> GetElementById(const CefString& id)

(source code url: https://bitbucket.org/chromiumembedded/cef/src/master/include/cef_dom.h?at=master&fileviewer=file-view-default)

So, to sum up: Since the CEF implemented methods to execute DOM manipulation, and CefSharp community seems to be very active and pushing frequent updates, and all information I could find on StackOverflow and Google are (kind of) outdated, does anyone knows if CefSharp currently (year 2020, version 85.3.130) already implemented any kind of DOM manipulation besides using JavaScript?

Vitox
  • 3,852
  • 29
  • 30
  • The information isn't outdated, JavaScript and DevTools protocol are your two options. The GetElementById method you are referring to exists only in the render processes, even if it was implemented you couldn't use it directly in your applications UI, see https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-processes for a description of the process model. – amaitland Nov 14 '20 at 19:12
  • See https://chromedevtools.github.io/devtools-protocol/tot/DOM/ and https://github.com/cefsharp/CefSharp/issues/3165 – amaitland Nov 14 '20 at 19:19
  • @amaitland about outdated information, i meant that that information was about previous versions of CefSharp, and a lot of development was done since then. Thus, it's old information than could not reflect the current state of the possibilities and features of CefSharp. – Vitox Nov 14 '20 at 19:58
  • @amaitland thank you for the links. I will certainly look through them carefully. – Vitox Nov 14 '20 at 19:59
  • @amaitland as I understands, you're one of the maintainers of this project, right? If it's so, I first wanna thank you for this great project, you did/do an awesome job. I can't thank you enough. Since it's you maintaining this project, I would really appreciate if you could post an actual answer, stating that the information about not direct dom manipulation is still correct/actual, and that there is those two options to do it instead. – Vitox Nov 14 '20 at 20:07
  • Yes, I am the current CefSharp maintainer. Thanks for the kind words. I'll try and post a more detailed answer in a little bit. I have to finish a few things before I can come back to this. DevTools support is very new and I haven't had a chance to test out the DOM methods, so there are no examples yet. – amaitland Nov 18 '20 at 04:49

1 Answers1

0

does anyone knows if CefSharp currently (year 2020, version 85.3.130) already implemented any kind of DOM manipulation besides using JavaScript?

As of 2022 there is an alternative to using JavaScript to access the DOM when using CefSharp. You can now use CefSharp.Dom which is an asynchronous library for accessing the DOM. The package requires CefSharp 104.4.180 or greater.

It uses the Chromium DevTools Protocol and originally started out as a port of Puppeteer Sharp.

It's freely available on Nuget.

It also supports keyboard, mouse and touch emulation for automating websites.

The following examples are an excerpt from the readme.

// Add using CefSharp.Dom to access CreateDevToolsContextAsync and related extension methods.
await using var devToolsContext = await chromiumWebBrowser.CreateDevToolsContextAsync();

// Get element by Id
// https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
var element = await devToolsContext.QuerySelectorAsync<HtmlElement>("#myElementId");

//Strongly typed element types (this is only a subset of the types mapped)
var htmlDivElement = await devToolsContext.QuerySelectorAsync<HtmlDivElement>("#myDivElementId");
var htmlSpanElement = await devToolsContext.QuerySelectorAsync<HtmlSpanElement>("#mySpanElementId");
var htmlSelectElement = await devToolsContext.QuerySelectorAsync<HtmlSelectElement>("#mySelectElementId");
var htmlInputElement = await devToolsContext.QuerySelectorAsync<HtmlInputElement>("#myInputElementId");
var htmlFormElement = await devToolsContext.QuerySelectorAsync<HtmlFormElement>("#myFormElementId");
var htmlAnchorElement = await devToolsContext.QuerySelectorAsync<HtmlAnchorElement>("#myAnchorElementId");
var htmlImageElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myImageElementId");
var htmlTextAreaElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myTextAreaElementId");
var htmlButtonElement = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId");
var htmlParagraphElement = await devToolsContext.QuerySelectorAsync<HtmlParagraphElement>("#myParagraphElementId");
var htmlTableElement = await devToolsContext.QuerySelectorAsync<HtmlTableElement>("#myTableElementId");

// Get a custom attribute value
var customAttribute = await element.GetAttributeAsync<string>("data-customAttribute");

//Set innerText property for the element
await element.SetInnerTextAsync("Welcome!");

//Get innerText property for the element
var innerText = await element.GetInnerTextAsync();

//Get all child elements
var childElements = await element.QuerySelectorAllAsync("div");

//Change CSS style background colour
await element.EvaluateFunctionAsync("e => e.style.backgroundColor = 'yellow'");

//Type text in an input field
await element.TypeAsync("Welcome to my Website!");

//Click The element
await element.ClickAsync();

// Simple way of chaining method calls together when you don't need a handle to the HtmlElement
var htmlButtonElementInnerText = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId")
    .AndThen(x => x.GetInnerTextAsync());

//Event Handler
//Expose a function to javascript, functions persist across navigations
//So only need to do this once
await devToolsContext.ExposeFunctionAsync("jsAlertButtonClick", () =>
{
    _ = devToolsContext.EvaluateExpressionAsync("window.alert('Hello! You invoked window.alert()');");
});

var jsAlertButton = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#jsAlertButton");

//Write up the click event listner to call our exposed function
_ = jsAlertButton.AddEventListenerAsync("click", "jsAlertButtonClick");

//Get a collection of HtmlElements
var divElements = await devToolsContext.QuerySelectorAllAsync<HtmlDivElement>("div");

foreach (var div in divElements)
{
    // Get a reference to the CSSStyleDeclaration
    var style = await div.GetStyleAsync();

    //Set the border to 1px solid red
    await style.SetPropertyAsync("border", "1px solid red", important: true);

    await div.SetAttributeAsync("data-customAttribute", "123");
    await div.SetInnerTextAsync("Updated Div innerText");
}

//Using standard array
var tableRows = await htmlTableElement.GetRowsAsync().ToArrayAsync();

foreach (var row in tableRows)
{
    var cells = await row.GetCellsAsync().ToArrayAsync();
    foreach (var cell in cells)
    {
        var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");
        await newDiv.SetInnerTextAsync("New Div Added!");
        await cell.AppendChildAsync(newDiv);
    }
}

//Get a reference to the HtmlCollection and use async enumerable
//Requires Net Core 3.1 or higher
var tableRowsHtmlCollection = await htmlTableElement.GetRowsAsync();

await foreach (var row in tableRowsHtmlCollection)
{
    var cells = await row.GetCellsAsync();
    await foreach (var cell in cells)
    {
        var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");
        await newDiv.SetInnerTextAsync("New Div Added!");
        await cell.AppendChildAsync(newDiv);
    }
}

amaitland
  • 4,073
  • 3
  • 25
  • 63