I have seen this for BHO extensions, where the JavaScript can call functions in the C++ BHO. But lets say I am not using a BHO, instead I have a C++ console application that creates an IE COM object like so:
HRESULT hr = CoCreateInstance(
CLSID_InternetExplorer,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2,
(void**)&_cBrowser);
I also have a class which "owns" the IWebBrowser2 object that comes back from this function.
class BrowserWrapper{
public:
CComPtr<IWebBrowser2> pBrowser;
void SomeFunction(...)
}
Is there a way to call a function like "SomeFunction" in the wrapper class from the JavaScript in the spawned IWebBrowser2 object?
You must implement the IDocHostUIHandler interface and set it to the web browser with a code similar to this (extracted from the doc):
ComPtr<IDispatch> spDocument;
hr = spWebBrowser2->get_Document(&spDocument);
if (SUCCEEDED(hr) && (spDocument != nullptr))
{
// Request default handler from MSHTML client site
ComPtr<IOleObject> spOleObject;
if (SUCCEEDED(spDocument.As(&spOleObject)))
{
ComPtr<IOleClientSite> spClientSite;
hr = spOleObject->GetClientSite(&spClientSite);
if (SUCCEEDED(hr) && spClientSite)
{
// Save pointer for delegation to default
m_spDefaultDocHostUIHandler = spClientSite;
}
}
// Set the new custom IDocHostUIHandler
ComPtr<ICustomDoc> spCustomDoc;
if (SUCCEEDED(spDocument.As(&spCustomDoc)))
{
// NOTE: spHandler is user-defined class
spCustomDoc->SetUIHandler(spHandler.Get());
}
}
You must specifically implement the GetExternal method
Now, in IE's javascript (or vbscript for that matter), you can access your host with a call like this:
var ext = window.external; // this will call your host's IDocHostUIHandler.GetExternal method
ext.SomeFunction(...); // implemented by your object
What you return in GetExternal must be an IDispatch object that you can design the way you want.
You need to implement the IDocHostUIHandler interface. This has a method called GetExternal - which you need to return an object that implements IDispatch.
In the javascript, you can call window.external.something() - which will cause the browser to query for your external implementation - the IDispatch object - and it will then use the IDispatch to execute something.
Related
I have loaded a webpage using WebView component and added a JavascriptInterface. Please check the code below,
val webview = WebView(this)
setContentView(webview)
webview.settings.javaScriptEnabled = true
webview.loadUrl(HOME_PAGE_URL)
webview.addJavascriptInterface(JavascriptInterface(),”javascript_bridge”)
And when I call the invoke from Javascript using window.javascript_bridge.showToast(“Information Saved”);
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: String?)
{
Log.d("WEBVIEW", text);
}
}
I am able to call the method from Javascript to Kotlin without any trouble.
But now I want to pass an Object from Javascript to Kotlin like below,
var info = {
message: “Information Saved”,
ID: 123456
}
And when I call the invoke from Javascript using window.javascript_bridge.showToast(info);
I tried to change to the data type to Any, but the value passed from Javascript is null
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: Any?)
{
Log.d("WEBVIEW", text.toString());
}
}
As far as i know, the javaScript interface methods only accepts primitive types of data as parameters (as discussed on this question).
If you still want to achieve that, you may serialize the object (to JSON format, for instance) in the javaScript and then Deserialize it in Java.
Hope it helps :)
I am working on an API in Java that allows users to write scripts and access a specific set of methods that are passed in (in the form of an API object) by the Nashorn script engine.
I want to, in the JavaScript, call a function getDate(), which will return some arbitrary date (as a native JavaScript date) that's provided from the Java side.
I have tried simply putting an org.java.util.Date on the API object, but that won't behave like a JS date. The goal is to make this as simple as possible for end-users who are experienced with JS.
Java Example:
public class MyAPI {
public void log(String text){
System.out.println(text);
}
public Date getDate(){
// Return something that converts to a native-JS date
}
public static void main(){
// MyClassFilter implements Nashorn's ClassFilter
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(new MyClassFilter());
((Invokable) engine).invokeFunction("entryPoint", new MyAPI());
}
JavaScript example
function entryPoint(myApi){
var date = myApi.getDate();
myApi.log(date.getMinutes());
}
jdk.nashorn.internal.* packages are nashorn internal implementation classes. There is no guarantee that these won't be changed or be removed between JDK versions. Besides with a security manager around, accessing these packages from java code directly would result in SecurityException being thrown! With jdk9 modular jdk, these packages are not exported packages from nashorn module and so javac won't even compile your code in jdk9!
I'd recommend using JS wrapper (solution 1) in the answer by user "ug_". If you do have to call from Java, you can use API exported from jdk.nashorn.api.scripting package.
If "engine" is your javax.script.ScriptEngine of nashorn, then you can do something like the following:
import jdk.nashorn.api.scripting.*;
..
public Object getDate() {
// get JS Date constructor object - you can get once and store
// as well/
JSObject dateConstructor = (JSObject) engine.eval("Date");
// now do "new" on it
return dateConstructor.newObject();
}
With that, your JS script can call "getDate()" on your API object and get a JS
Date object. Note that you can also pass constructor arguments to newObject
method call (it is a Java variadic method).
The Nashorn engine has objects it uses internally which represent the Javascript objects. As you have guessed the java.util.Date != new Date() (in javascript). The engine uses a class called jdk.nashorn.internal.objects.NativeDate to represent its JS date.
If I were building this out I would not have the NativeDate constructed in the Java but instead have a wrapper in Javascript for the MyApi object which would contain a few other native JS methods, such as getDate().
var MYAPI_JAVASCRIPT = {
log: function() {
print(arguments);
},
getDate: function() {
return new Date();
}
}
You could then pass that object as the method parameter.
However if your really set on using the NativeDate in your Java code then you can construct one like so:
public NativeDate getDate() {
return (NativeDate) NativeDate.construct(true, null);
}
As far as I know we can call C# function from Javascript, that is loaded inside a WebBrowser control, following code shows how I usually do it.
Form1.cs
public partial class Form1 : Form{
private WebBrowser webBrowser1;
public ApplicationWindow(){
InitializeComponent();
WebBrowser webBrowser1 = new WebBrowser();
//some code follows
webBrowser1.ObjectForScripting = new ScriptManager();
this.webBrowser1.Url = new Uri("file:///d:/ui/application.html");
}
}
}
ScriptManager.cs
namespace WindowsFormsApplication10 {
[ComVisible(true)]
public class ScriptManager{
public string GetAllDomains(){
string result=null;
//does something;
return result;
}
}
}
application.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
var result = window.external.GetAllDomains();
//it works but this is what puzzles me.
});
</script>
The questions that intrigues me are
why we need ComVisible to be true for class whose object we are going to use as objectForScripting?
How Javascript object window.external has the same methods as in objectForScripting?
How they handle cross language type conversion?
I wonder why no one answered for so long. The answer to all your questions is COM - Component Object Model.
Windows is providing ability (using COM) for classes and functions from one program (exe) to be accessible outside the exe.
So
1) why we need ComVisible to be true for class whose object we are going to use as objectForScripting?
-> This tells windows to make the class and its methods visible to the webbrowser.
2) How Javascript object window.external has the same methods as in objectForScripting?
-> The javascript is calling methods of the class made visible in above answer.
3) How they handle cross language type conversion?
-> COM handles the types internally so methods in one programming language can be called from another programming language.
I am currently working on an IE-only project which makes use of an external object model provided by the host application. Internet Explorer allows access to this external object through browser components:
http://msdn.microsoft.com/en-us/library/ie/ms535246(v=vs.85).aspx
Access to the object takes the form of JavaScript function invocations, similar to:
external.MethodName(arg1, arg2);
One of the recent changes to the application flow will introduce hundreds, if not thousands of if-statement conditionals around these JavaScript invocations, e.g.:
if (X) {
external.MethodName(arg1, arg2);
} else {
// do something else
}
Rather than modify potentially thousands of HTML files, it would seem to make sense if we could override or rewrite the external object's functions so that the if condition only appears in one place. Normally, this could be accomplished in JavaScript with:
external.OldMethodName = external.MethodName;
external.MethodName = function(arg1, arg2) {
if (X) {
external.OldMethodName(arg1, arg2);
} else {
// do something else
}
};
However, this results in an "Invalid procedure call or argument" script error, because you cannot reference the external host method this way.
I do not have access to the host application proprietary code to change the external method directly.
Is there any way I can use JavaScript to override the external object's functions, or will I need to wrap the (potential) thousands of invocations with if-statements (a very bad practice)?
UPDATE: After much back-and-forth with the client, we have managed to reach out to the third-party vendor to update the external host method, which is vastly preferable to our method of wrapping the method on the front end. I have accepted Paul's answer in the meantime.
Use toString() and eval:
var foo = external.MethodName.toString().replace("OldMethodName", "MethodName").replace("bar","baz");
eval(foo);
if(x)
{
external.OldMethodName(arg1,arg2);
}
else
{
MethodName(arg1,arg2)
}
Awesomium easily allows for C++ code to call Javascript methods, but I haven't found a definite answer as to if it can do the opposite. This site seems to say that you can, but looking through the text and examples doesn't enlighten me.
So, I'm looking for a definite answer: can I call C++ variables/methods in my Javascript(Jquery), or not?
If you could include a simple example, that would be extremely appreciated as well.
Thank you!
You definitely can-- you'll just need to build an extra layer on top of WebView::setObjectCallback and WebViewListener::onCallback using delegates/function-pointers.
I wrote a quick JSDelegate.h class (view it here) that you can use to hookup "onCallback" events directly to C++ member functions.
The basic idea is to maintain a mapping of callback names to delegates:
typedef std::map<std::wstring, Awesomium::JSDelegate> DelegateMap;
DelegateMap _delegateMap;
And call the corresponding function from your WebViewListener::onCallback:
void MyListener::onCallback(Awesomium::WebView* caller, const std::wstring& objectName,
const std::wstring& callbackName, const Awesomium::JSArguments& args)
{
DelegateMap::iterator i = _delegateMap.find(callbackName);
if(i != _delegateMap.end())
i->second(caller, args);
}
And then, each time you wish to bind a specific C++ function, you would do it like so:
// Member function we wish to bind, must have this signature for JSDelegate
void MyClass::myFunction(Awesomium::WebView* caller, const Awesomium::JSArguments& args)
{
// handle args here
}
// Instantiate MyClass instance in C++
MyClass* myClass = new MyClass();
// Create corresponding 'MyClass' object in Javascript
webView->createObject(L"MyClass");
// Do the following for each member function:
// Bind MyClass::myFunction delegate to MyClass.myFunction in JS
_delegateMap[L"myFunction"] = Awesomium::JSDelegate(myClass, &MyClass::myFunction);
webView->setObjectCallback(L"MyClass", L"myFunction");
Then, you should be able to call MyClass::myFunction directly from Javascript like so:
MyClass.myFunction("foo", 1, 2 3)
Hope this helps! I haven't tested any of the code but I wrote it with Awesomium v1.6 RC4 SDK in mind.