Page 1 of 2

Return string result from ExecuteJavaScript.

PostPosted: Thu Nov 25, 2010 12:39 pm
by chillitom
It would be fantastic if there was an alternative to CefFrame's ExecuteJavaScript (perhaps called EvaluateJavaScript) which would return the result as a string.

V8's shell (http://code.google.com/p/v8/source/brow ... s/shell.cc) does this to the results of it's evaluation:

Code: Select all
 v8::String::Utf8Value str(result);

Re: Return string result from ExecuteJavaScript.

PostPosted: Fri Nov 26, 2010 12:02 pm
by magreenblatt
Returning a value from ExecuteJavaScript would require blocking the calling thread. This is a bad idea in most cases but especially in situations where the execution may take a while, as with executing arbitrary JS code. A better option is to have ExecuteJavaScript call back to your code asynchronously. This can be done currently by exposing native function(s) via window binding or V8 extension that the JS code passed to ExecuteJavaScript can call when the task is completed.

Re: Return string result from ExecuteJavaScript.

PostPosted: Fri Dec 03, 2010 4:34 am
by chillitom
I agree that blocking isn't always the nicest thing to do but consider this case:

I wish to execute some JavaScript and await the result. If everything goes well the script completes and the result can be passed to an exposed native function, the native function can in turn signal a wait handle to let the waiting thread know that the operation has completed, all well and good.

Now consider that there is a problem with the JavaScript, an exception is raised before the code completes, the native function never gets called and the waiting thread will hang indefinitely.

Some problems can be caught using a try/catch so we could have something like this:

Code: Select all
try
{
// <script> is the inlined contents of the CefFrame->ExecuteJavaScript's jsCode argument.
__native_result_function(<script>);       
}
catch(e)
{
__native_error_function(e);
}


However if <script> is ill formed (e.g. one too many { ) then the script error might never get caught.

If a synchronous api call is out of the question then I'd suggest adding success and failure callbacks be added to the method signature.

Thoughts?

Re: Return string result from ExecuteJavaScript.

PostPosted: Fri Dec 03, 2010 9:36 am
by magreenblatt
Your application should not block waiting for JavaScript execution to complete. Instead, it should await notification of JavaScript completion asynchronously.

Any errors in JavaScript parsing should be reported to CefHandler::HandleConsoleMessage. Just specify a unique value for the |sciptUrl| argument to CefFrame::ExecuteJavaScript. This notification can then be used by your application to perform any required cancellation actions.

Re: Return string result from ExecuteJavaScript.

PostPosted: Fri Dec 03, 2010 12:39 pm
by chillitom
Okay I agree with you on the asynchronous stuff but would it not be possible to have some explicit callback for the completion of a JavaScript call? Only it seems a little awkward trying to repurpose the javascript console message handler for this purpose.

Something like this would be ideal:

Code: Select all
class CefV8AsyncResult
{
   void Success(CefRefPtr<CefV8Value> result, void* user_data) =0;
   void Error(CefRefPtr<CefV8Value> exception, void* user_data) =0;
}

with

Code: Select all
void CefFrame::ExecuteJavaScript(CefString script, CefString scriptName, int lineOffset, CefRefPtr<CefV8AsyncResult> resultHandler, void* user_data);


I'm not familiar with the internals of Chromium, perhaps this isn't possible, what do you think?

Re: Return string result from ExecuteJavaScript.

PostPosted: Fri Dec 03, 2010 1:47 pm
by magreenblatt
I'm not familiar with the internals of Chromium, perhaps this isn't possible, what do you think?

Nothing is impossible, but I'm not sure this situation is well defined. What happens if the JavaScript code sets a timer that gets executed later on? Is the JavaScript considered complete only after the timer fires?

Regards,
Marshall

Re: Return string result from ExecuteJavaScript.

PostPosted: Mon Dec 06, 2010 12:49 pm
by chillitom
In my opinion the callback should get run when the evaluation of the code provided to the method call finishes, if the code has set up timers then these should still run but aren't of concern to the callback. This would make the call's behavior similar to a number of other embeddable/scriptable browser controls, e.g. MSHTML, WebDriver or Watin/Watir.

This functionality can be very useful for automating site testing. You can drive the browser using JavaScript and use more JavaScript to check assertions about the page content but knowing when an action has completed is very important as tests need to be run in the correct order/timing.

Re: Return string result from ExecuteJavaScript.

PostPosted: Mon Dec 06, 2010 1:39 pm
by magreenblatt
In my opinion the callback should get run when the evaluation of the code provided to the method call finishes, if the code has set up timers then these should still run but aren't of concern to the callback.

OK, I agree. It's probably best to provide the same error information to ExecuteError that is currently passed to CefHandler::HandleConsoleMessage.

Code: Select all
class CefV8ExecutionStateListener : public CefBase
{
  virtual void ExecuteSuccess(CefRefPtr<CefV8Value> result) =0;
  virtual void ExecuteError(const CefString& message, const CefString& source, int line) =0;
};

void CefFrame::ExecuteJavaScript(const CefString& jsCode, const CefString& scriptUrl,
    int startLine, CefRefPtr<CefV8ExecutionStateListener> stateListener);

Should we also have an ExecuteStarting method that's called immediately before the JS execution begins and gives the user one last opportunity to cancel the execution?

Re: Return string result from ExecuteJavaScript.

PostPosted: Tue Dec 07, 2010 5:34 am
by chillitom
I like the API you've suggested, the void* to user data that I suggested was redundant and dangerous, no ref counting.

Personally I can't immediately think of cases where I'd make use of an ExecuteStarting callback to cancel execution, others might though. Having a callback just before execution would be useful for measuring performance precisely. If it's easy to implement and you think people might find that useful they I'd say go for it.

Re: Return string result from ExecuteJavaScript.

PostPosted: Tue Dec 07, 2010 9:28 am
by magreenblatt
I've created an issue to track this:
http://code.google.com/p/chromiumembedd ... ail?id=151

Patches are welcome.