AutoBinding CefSharp.BindObjectAsync in IRenderProcessMessageHandler:OnContextCreated - javascript

using CEFSharp i try to autoinject a bound Object in the IRenderProcessMessageHandler:OnContextCreated Method but i guess there is a await missing:
_chromeProxy = new ChromeProxy();
chromiumWebBrowser1.RenderProcessMessageHandler = new RenderProcessMessageHandler();
chromiumWebBrowser1.JavascriptObjectRepository.ObjectBoundInJavascript += (mysender, mye) =>
{
var name = mye.ObjectName;
Console.WriteLine($"Object {mye.ObjectName} was bound successfully."); //Gets triggered
};
chromiumWebBrowser1.JavascriptObjectRepository.Register("App", new ChromeProxy(), true);
and the OnContextCreated Method:
public void OnContextCreated(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
{
// Does bind but too late ( no await )
const string script = "CefSharp.BindObjectAsync('App');";
frame.ExecuteJavaScriptAsync(script);
// ERR: Uncaught SyntaxError: await is only valid in async function
// const string script = "await CefSharp.BindObjectAsync('App');";
// frame.ExecuteJavaScriptAsync(script);
}
Is it possible to register the bound Object with this approach and if, how could i await the result of the binding before Javascript on the page gets executed?

thanks to #amaitland i got it working:
the key was to use EvaluateScriptAsync instead of ExecuteJavaScriptAsync with the following iife
frame.EvaluateScriptAsync(#"(async function() {await CefSharp.BindObjectAsync('App');})();");

Related

Chrome Extension: How can I access the new document on page changes?

I'm trying to amend the DOM using a chrome extension. Unfortunately I fail with accessing the new page after some of the actions cause a page change. My code looks like this:
content_script.js
(async () => {
try {
execute_edit = async () => {
console.log(document.title)
const el = document.getElementsByClassName("icon")
const first_el = el[0]
first_el.click()
}
change_element = async () => {
console.log(document.title)
const el = document.querySelector("element")
console.log(el.innerHTML)
}
await execute_edit()
await change_element()
} catch (e) {
console.log(e)
}
})();
I'm getting the error that the "el" in "change_element" does not exist. This is obviously caused by the fact that both "document.title" are identical i.e. the second function still tries to access the original DOM and not the new page.
Any suggestions on how I can have the second function "change_element" access the new page's DOM?

Check string contains JavaScript code or normal string?

There is a situation to run code inside javascript string. But there is two possibilities that the string can contain a javascript or not. So I want to check first is there any javascript code before executing the string.
let code = "alert('Run Run')";
let runFunction = new Function(code);
runFunction();
I tried regex apporach, but it would not work.
let exists = code.match(/[!##$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/) ? "YES" : "NO";
Is there any way to check this.
You can check the syntax of the string to see if it can be parsed properly before running it for real, though this essentially just involves attempting to execute the string as Javascript anyway.
const stringIsJS = (str) => new Promise((resolve) => {
window.addEventListener('unhandledrejection', (errorEvent) => {
// Don't pollute the console with additional info:
errorEvent.preventDefault();
resolve(errorEvent.reason.message === 'No syntax problems');
}, { once: true });
Promise.resolve().then(() => {
eval('throw new Error("No syntax problems");' + str);
});
});
(async () => {
console.log(
await stringIsJS('hello hello'),
await stringIsJS('const foo = "bar";'),
);
})();
throw new Error is inserted at the top of the code to be evaluated to ensure that the stringIsJS only checks the syntax of the string, rather than actually running any substantive code.
Or, using Acorn:
const stringIsJS = (str) => {
try {
acorn.Parser.parse(str);
return true;
} catch(e){
return false;
}
};
console.log(
stringIsJS('hello hello'),
stringIsJS('const foo = "bar";'),
);
<script src="https://cdn.jsdelivr.net/npm/acorn#6.1.1/dist/acorn.min.js"></script>
(still, this is a very weird thing to have to determine programmatically - it would be much better to, for example, save an additional flag in a database to indicate whether a particular string is Javascript or not)

Load Tensorflowjs from json object not json

I am trying to load a Tensorflowjs model using the model.json which is an in memory browser side object.
https://js.tensorflow.org/api/latest/#loadLayersModel
One approach may be to return the json from a dummy fetch method.
fetchFunc (Function) A function used to override the window.fetch
function.
Alternatively, it is possible to create a custom IOHandler, but there is very little documentation on this.
An tf.io.IOHandler object that loads model artifacts with its load
method.
Does anyone know how to achieve this using the tensorflow load methods?
var modelJson = "{...ModelAndWeightsConfig}";
//Do something here to load it.
var newModel = tf.loadLayersModel("/model_0/model.json", {
onProgress: onProgressCallback}).then(model =>{});
Regards,
Yes, you can write your own IOHandler to load the model. Check out the definition of an IOHandler here. You have to implement the load function that returns a Promise<ModelArtifacts>.
That means, to load a model saved by the file IOHandler, you can check out the source code and reimplement the load function yourself.
Code Sample
Here is example to get you started. The load() part is mostly copied from the loadJSONModel function from the file IOHandler. Basically, the JSON string is passed as an argument and then used when the load function is called by Tensorflow.js.
export class JSONHandler implements tfc.io.IOHandler {
constructor(jsonString) {
this.jsonString = jsonString;
}
async load() {
const modelJSON = JSON.parse(jsonString);
const modelArtifacts: tfc.io.ModelArtifacts = {
modelTopology: modelJSON.modelTopology,
format: modelJSON.format,
generatedBy: modelJSON.generatedBy,
convertedBy: modelJSON.convertedBy
};
if (modelJSON.weightsManifest != null) {
// load weights (if they exist)
}
if (modelJSON.trainingConfig != null) {
modelArtifacts.trainingConfig = modelJSON.trainingConfig;
}
if (modelJSON.userDefinedMetadata != null) {
modelArtifacts.userDefinedMetadata = modelJSON.userDefinedMetadata;
}
return modelArtifacts;
}
}
To use the model, you can then create an instance of it and pass it to the load function:
const modelJson = '{ ... }';
const handler = new JSONHandler(modelJson);
const model = await tf.loadLayersModel(handler);
Fetch the models
var fetchPromise = function(url,p1,p2,) {
return new Promise(function(resolve, reject) {
fetch(url)
.then(response => {
resolve(response);
}).catch(err =>{
reject();
});
});
};

IndexedDB's callbacks not being executed inside the 'fetch' event of a Service Worker

I'm trying to do a couple of things in the IndexedDB database inside the 'fetch' event of a service worker, when the aplication asks the server for a new page. Here's what I'm going for:
Create a new object store (they need to be created dynamically, according to the data that 'fetch' picks up);
Store an element on the store.
Or, if the store already exists:
Get an element from the store;
Update the element and store it back on the store.
The problem is that the callbacks (onupgradeneeded, onsuccess, etc) never get executed.
I've been trying with the callbacks inside of each other, though I know that may not be the best approach. I've also tried placing an event.waitUntil() on 'fetch' but it didn't help.
The 'fetch' event, where the function registerPageAccess is called:
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
event.waitUntil(function () {
const nextPageURL = new URL(event.request.url);
if (event.request.destination == 'document') {
if (currentURL) {
registerPageAccess(currentURL, nextPageURL);
}
currentURL = nextPageURL;
}
}());
/*
* some other operations
*/
return response || fetch(event.request);
})
);
});
registerPageAccess, the function with the callbacks.
I know it's plenty of code, but just look at secondRequest.onupgradeneeded in the 5th line. It is never executed, let alone the following ones.
function registerPageAccess(currentPageURL, nextPageURL) {
var newVersion = parseInt(db.version) + 1;
var secondRequest = indexedDB.open(DB_NAME, newVersion);
secondRequest.onupgradeneeded = function (e) {
db = e.target.result;
db.createObjectStore(currentPageURL, { keyPath: "pageURL" });
var transaction = request.result.transaction([currentPageURL], 'readwrite');
var store = transaction.objectStore(currentPageURL);
var getRequest = store.get(nextPageURL);
getRequest.onsuccess = function (event) {
var obj = getRequest.result;
if (!obj) {
// Insert element into the database
console.debug('ServiceWorker: No matching object in the database');
const addRes = putInObjectStore(nextPageURL, 1, store);
addRes.onsuccess = function (event) {
console.debug('ServiceWorker: Element was successfully added in the Object Store');
}
addRes.onerror = function (event) {
console.error('ServiceWorker error adding element to the Object Store: ' + addRes.error);
}
}
else {
// Updating database element
const updRes = putInObjectStore(obj.pageURL, obj.nVisits + 1, store);
updRes.onsuccess = function (event) {
console.debug('ServiceWorker: Element was successfully updated in the Object Store');
}
updRes.onerror = function (event) {
console.error('ServiceWorker error updating element of the Object Store: ' + putRes.error);
}
}
};
};
secondRequest.onsuccess = function (e) {
console.log('ServiceWorker: secondRequest onsuccess');
};
secondRequest.onerror = function (e) {
console.error('ServiceWorker: error on the secondRequest.open: ' + secondRequest.error);
};
}
I need a way to perform the operations in registerPageAccess, which involve executing a couple of callbacks, but the browser seems to kill the Service Worker before they get to occur.
All asynchronous logic inside of a service worker needs to be promise-based. Because IndexedDB is callback-based, you're going to find yourself needing to wrap the relevant callbacks in a promise.
I'd strongly recommend not attempting to do this on your own, and instead using one of the following libraries, which are well-tested, efficient, and lightweight:
idb-keyval, if you're okay with a simple key-value store.
idb if you're need the full IndexedDB API.
I'd also recommend that you consider using the async/await syntax inside of your service worker's fetch handler, as it tends to make promise-based code more readable.
Put together, this would look roughly like:
self.addEventListener('fetch', (event) => {
event.waitUntil((async () => {
// Your IDB cleanup logic here.
// Basically, anything that can execute separately
// from response generation.
})());
event.respondWith((async () => {
// Your response generation logic here.
// Return a Response object at the end of the function.
})());
});

TypeError: Method Promise.prototype.then called on incompatible receiver Proxy

to perform integration testing I used jasmine and puppeteer and since I am passing an educational course ,according to that ,I decided to use a js Proxy to encapsulate testing functionality but when I do my test I will encounter with the following error
TypeError: Method Promise.prototype.then called on incompatible receiver [object Object]
here is my CustomPage class which is going to represent a chrome tab :
const puppeteer = require('puppeteer');
class CustomPage{
static async build(){
const browser =await puppeteer.launch({headless:false});
const page = browser.newPage();
var customPage = new CustomPage(page);
console.log("harchi run mishe")
return new Proxy(customPage,{
get:function(target,property){
return (customPage[property]||page[property]||browser[property])
}
})
//return proxy;
}
constructor(page){
this.page = page
}
}
module.exports=CustomPage;
and here is my header.spec.js file which is my test file.
const Page = require('./helpers/page');
var tab;
describe('header representation',()=>{
beforeEach(async(done)=>{
tab =await Page.build();****here is the problem********
await tab.goto('localhost:3000');
})
it('should show header logo',async()=>{
const text = await tab.$eval('a.brand-logo',(el)=>el.innerHTML);
expect(text).toEqual('Blogster');
//done()
})
})
I have actually convicted that my problem is with the specified line .it seems that js can't treat proxy as a Promise however I couldn't find any solution to that.
For posterity, I discovered that with Proxies you need to rebind javascript's this keyword. Example:
function validator(promise, prop) {
if (prop in promise || promise.hasOwnProperty(prop)) {
if (typeof promise[prop] === 'function') {
return promise[prop].bind(promise); // << Important part!
}
return promise[prop];
}
return 'Fake!';
}
const proxy = new Proxy(
Promise.resolve('Testing 1 2 3'),
validator
);
console.log(proxy.someFakeThing); // prints 'Fake!'
proxy.then(console.log); // Prints 'Testing 1 2 3'

Categories