Does CefSharp implemented DOM manipulation (late 2020)? - cefsharp

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?

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 .
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);
}
}

Related

I'm having trouble viewing user comments in Flutter with Firebase

I'm trying to sort the user's comments on his profile page but the code doesn't work right what's wrong here please
List<Widget> myComments = [];
posts.data.docs.forEach((post) {
cloudRef.collection('blog').doc(post.id).collection('comments').where('ownerID',isEqualTo:id).get().then((val){
val.docs.forEach((comment) {
myComments.add(CommentCard(replyContent: comment.data()));
});
});
});
return myComments;
The issue with the code is that forEach is used with an asynchronous operation. forEach does not wait for the async operations to complete for each loop.
Checkout this update to the code using for-in. I also updated the comment card list to use .map so you can have it in one list and then add all the comments at once.
List<Widget> myComments = [];
for (var post in posts.data.docs) {
var val = await cloudRef.collection('blog').doc(post.id).collection('comments').where('ownerID',isEqualTo:id).get();
var commentCardList = val.docs.map((comment) => CommentCard(replyContent: comment.data())).toList();
myComments.addAll(commentCardList);
}
return myComments;

For loop only executes once when calling function in flutter

This is my first experience with Flutter, just trying to create a super simple app that uploads a bunch of photos to firebase-storage to assist our media & marketing teams internally.
I have successfully implemented multi_image_picker, I can select images and build out the grid view, the images are stored in a list.
I have an upload button, when pressed it calls a uploadImages Function which should loop over the list of images created by multi_image_picker and call the saveImage function to save the images to Firebase.
While it triggers, it only ever uploads a single image and never the entire list as such, the for looop never seems to keep running.
Button to Call uploadImages function:
RaisedButton(
child: Text("Upload Images"),
onPressed: () {
uploadImages(images);
},),
uploadImages function with for loop:
void uploadImages(List<Asset> images) async {
for (var i = 0; i < images.length; i++) {
await saveImage(images[i]);
}
}
Finally the saveImage function that works, but only ever uploads a single image:
Future saveImage(Asset i) async {
ByteData byteData = await i.getByteData();
List<int> imageData = byteData.buffer.asUint8List();
StorageReference ref =
FirebaseStorage.instance.ref().child("$now " + "$displayName.jpg");
StorageUploadTask uploadTask = ref.putData(imageData);
StorageTaskSnapshot storageSnapshot = await uploadTask.onComplete;
var downloadURL = await storageSnapshot.ref.getDownloadURL();
if (uploadTask.isComplete) {
final String url = downloadURL.toString();
return url;
} else {
print('Error from image repo ${storageSnapshot.error.toString()}');
}}
I've tried to many different things, found a few errors with debugger and resolved, but after days of trying I am just unable to get it to keep uploading the images per the for loop. Can someone see what I have done wrong here?

How to retrieve value of child data from firebase DB?

I am trying to grab the children data from a certain movie title in a database, but I can only get an output of "Instance of Datasnapshot."
Here is the setup of the DB with the highlighted information I am trying to store in a list:
I tried using the following code with no success:
Future<List<String>> getMovieDetails(String movieName) async {
DataSnapshot movies = await DBRef.child("Movies").child(movieName).once().then((DataSnapshot datasnapshot)
{
print(datasnapshot.value.toString());
});
var moviesMap = Map<String, dynamic>.from(movies.value);
List<String> moviesList = [];
moviesMap.forEach((key, value){
moviesList.add(key);
print('My-Key $key');
print('Movie List: $moviesList');
});
return moviesList;
}
Note: I am passing the selected movie name so I only grab the child information from the movie the user selects. This portion is correctly, if the user clicks on the list tile of Batman, the title will be passed to this getMovieDetails() function.
Try the following:
Future<List<String>> getMovieDetails(String movieName) async {
DataSnapshot movies = await FirebaseDatabase.instance
.reference()
.child("Movies")
.child(movieName)
.once();
var moviesMap = Map<String, dynamic>.from(movies.value);
List<String> moviesList = [];
moviesMap.forEach((key, value) {
moviesList.add(value);
print('My-Key $key');
print('My-Value $value');
});
return moviesList;
}
}
You dont have to use then() since you are using await. Also when you call this method, you need to do for example:
await getMovieDetails("Batman");
I will make the above answer as correct, but the biggest issue was when I did:
moviesList.add(key)
When it should be:
moviesList.add(value)

React Native base64 Image Upload to Firebase Storage

I am currently working on an App. The workflow I currently have is fairly simple.
A user creates an account, and then is taken to a page to populate their profile information. Name, description, and a few images.
I use expo's ImagePicker to get the image:
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.1,
allowsEditing: true,
aspect: [2, 3],
base64: true
});
Originally, I was using this to upload the images:
// Why are we using XMLHttpRequest? See:
// https://github.com/expo/expo/issues/2402#issuecomment-443726662
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(xhr.response);
};
xhr.onerror = function(e) {
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
const ref = firebase
.storage()
.ref()
.child(uuid.v4());
const snapshot = await ref.put(blob);
// We're done with the blob, close and release it
blob.close();
let url = await snapshot.ref.getDownloadURL();
return url;
The problem here is I looped through that function about 6 times, and I kept getting some obscure error.
Currently, I am attempting to upload the images using this:
const ref = firebase
.storage()
.ref()
.child(uuid.v4());
const snapshot = await ref.putString(b64Url, "data_url");
This works well on web, but in the native app I get the error:
FirebaseStorageError {
"code_": "storage/invalid-format",
"message_": "Firebase Storage: String does not match format 'base64': Invalid character found",
"name_": "FirebaseError",
"serverResponse_": null,
}
The last comment on this issue outlines the problem. To break it down: atob doesn't exist. This is the sole problem behind the error. To fix, I polyfilled it like this:
import { decode, encode } from "base-64";
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
However, the second problem is that:
Firebase also tries to use the native Blob class (implemented by react-native), but the react-native version of Blob incorrectly converts the Uint8Array data to a string, corrupting the upload.
I tried his solution of deleteing global.Blob and restoring it after the upload. Firebase must have become dependent upon blob though, because now it errors out since Blob doesn't exist. Edit: Blob is actually being called somewhere in AppEntry.bundle, the uploading works correctly.
I would like to keep my app in a managed workflow, so I would very much prefer not to eject.
My questions are as follows:
Where specifically in react-native is the broken Blob code that:
incorrectly converts the Uint8Array data to a string
Is there a way that I can, while avoiding errors or ejecting, upload 6 images at once to firebase storage? If so, how?
The solution I ended up following was this:
async function uploadImageAsync(uri) {
const ref = firebase
.storage()
.ref()
.child(uuid.v4());
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(xhr.response);
};
xhr.onerror = function() {
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
var mimeString = uri
.split(",")[0]
.split(":")[1]
.split(";")[0];
const snapshot = await ref.put(blob, { contentType: mimeString });
let url = await snapshot.ref.getDownloadURL();
return url;
}
I found that I could not seem to get firebase's putString function to work, but I could create a blob out of the string using XMLHttpRequest. Then I just upload the blob to firebase

Issue adding element to list

I'm a total newbie to Flutter and I'm trying to add some data from Cloud Firestore to a list in Flutter, but having issues. I try to add the element, but after executing, the element isn't there. It's not throwing an exception or anything either. Maybe someone else has some advice for me!
I have tried changing the type of list (capture the doc from Cloud Firestore instead of data within the doc, same issue), I have also debugPrinted the data I am trying to store to make sure it exists, it does. I have done basic troubleshooting like running flutter clean as well. I am on the latest version of Flutter.
Firestore db = firestore();
List<String> getString() {
var dataList = new List<String>();
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
});
debugPrint(dataList.first);
return dataList;
The list is empty, though it should contain the "First name" field on this Cloud Firestore doc. Again, verified the data does exist and prints when calling debugPrint.
The db.collection('Users').get() is a async function, so debugPrint(dataList.first); executes before of the end of your firestores get, because that your array returns empty.
If you try it:
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
});
You will see your data.
You can use await to wait the call finishes, so you must return a Future and use async key word on function declaration. This is a conceipt that you must know of flutter async functions (Async Flutter). So, the code below can solve your problem.
Firestore db = firestore();
Future <List<String>> getString() async {
var dataList = new List<String>();
var result = await db.collection('Users').get();
result.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
return dataList;
}

Resources