As Qt's documentation is far from complete, which are the ways to setup QWebChannel and are there different ones from adding a <script> tag in your HTML page like recommended in the documentation?
There are different options to load the needed script accessible at the URL qrc:///qtwebchannel/qwebchannel.js:
Notice:
you can only use that URL if you are viewing the page from within QWebEngineView. Otherwise you might need to load the API file to your webserver.
Prerequisites:
add QT += webchannel to your .pro file and run qmake
Using runJavaScript() to execute the code in Qt's QWebChannel JS API:
My prefered method because it seems simple and has been reliable so far.
Keep in mind the possibility that the API might not be set up by the time this code has finished as the runJavaScript() method does not block the current thread until the JS code has been executed.
QFile apiFile(":/qtwebchannel/qwebchannel.js"); //load the API from the resources
if(!apiFile.open(QIODevice::ReadOnly))
qDebug()<<"Couldn't load Qt's QWebChannel API!";
QString apiScript = QString::fromLatin1(apiFile.readAll());
apiFile.close();
QWebEngineView view; //your custom QWebEngineView
view.page().runJavaScript(apiScript);
Using runJavaScript() to execute code to load Qt's QWebChannel JS API:
Although I didn't try these methods they should still work, as they aim to load external JS files.
One of the methods suggested here might come in handy depending on which other libraries you use.
Using a <script> tag in the web page:
This is the default method recommended by Qt's documentation. It has the advantage of providing the API immediatly after the page was loaded.
The downside of this is that you need to modify the HTML of the web page which can be painful if you are not the owner of the site.
Don't forget create a QWebChannel object on the C++ side:
QWebChannel* channel_ = new QWebChannel(view.page());
//attach it to the QWebEnginePage
view.page()->setWebChannel(channel_);
//register a QObject to be exposed to JavaScript
channel_->registerObject(QStringLiteral("jshelper"), this);
Set up the QWebChannel on the JavaScript code:
As pointed out in the comments in order to share objects between C++ and JavaScript you need to initialize the QWebChannel.
new QWebChannel(qt.webChannelTransport, function (channel) {
var sharedObject = channel.objects.sharedObject;
});
This answer was thought as an addition to Qt's documentation, so if you want to add something feel free to do so.
Version: Qt 5.6.1
Sources:
QT QWebEnginePage::setWebChannel() transport object
How do I include a JavaScript file in another JavaScript file?
How to use Qt WebEngine and QWebChannel?
http://doc.qt.io/qt-5/qtwebchannel-javascript.html
Related
I'm looking into using the WebView2 component for rendering some UI things on Windows, and I have a question about resource loading: for loading "normal" resources (i.e. HTML, CSS, images, JavaScript, whatever), the component mostly takes care of handling the loading of those resources itself. But I wonder if there is a way to hook into that loading process and control it yourself with WebView2?
As an example: say you want to load an image that is procedurally generated on native side of the WebView2, i.e. it is not in a file or on a server. How would you do that? Another example would be if you stored all your resources in a zip file or a SQLite database or something like that, and you wanted the WebView2 component to load resources directly from it (with you writing the "bridge" code yourself), how would you do that?
The reason I'm asking is because on macOS, WKWebView provides exactly this functionality by allowing you to create custom url schemes and hooking up handlers to them. This is exactly what I want, and it allows you to do something like this in your HTML:
<script type="text/javascript" src="my-scheme://test.js"/>
And on the Objective-C side, you can do a thing like this (leaving out boilerplate for hooking up my-scheme to this handler, this is the meat of the code for handling the response):
const char* data = "function testFunction() { return 18; }";
[task didReceiveResponse: [[NSURLResponse alloc]
initWithURL: task.request.URL
MIMEType: #"text/javascript"
expectedContentLength: strlen(data)
textEncodingName: #"ASCII"]];
[task didReceiveData: [NSData
dataWithBytes: data
length:strlen(data)]];
[task didFinish];
I.e. by registering the custom url scheme handler, I could send over my C string there as a JavaScript file. It doesn't have to be a hard-coded C string, obviously, as I mentioned the most relevant uses for me would be to provide procedurally generated resources, as well as loading things that are not necessarily stored as files (or on a web server). How would you do this with WebView2?
One way I have sent any type of file from native code to a browser is by converting the file to Base64 and then sending it to the browser. In the case of WebView2 you could use ExecuteScriptAsync. Once the Base64 string is received you could have javascript code (which you have previously injected) to convert it into a blob/file and then you can add it to any part of the DOM you want.
I have created several Three.js/Javascript demo applications that I'm experimenting with in my new Oculus Go. I'm trying to enable the Go Controller to do stuff in my applications, and according to the Oculus Developer Center, the best thing to do is to include OVRManager in my scene so I have access to that API. That sounds good, but for all the documentation (https://developer.oculus.com/documentation/unity/latest/concepts/unity-ovrinput/) I can't see HOW to add OVRManager to my scene! I have not worked with Unity before, but from what I can tell in the documentation there shouldn't be any compatibility issues (should there?)
So what I'd think to do is something like:
<script src="OVRManager.js or something like that"></script>
and then call the functions I need, as I've done with OrbitControls.js and other external dependencies.
But for the life of me, Google searching is just sending me in circles. I see questions posed for C++ and C# but that's of no use to me. How do I get this API working in my Three.js scene? Where do I find it and is there some other way to include it?
Thanks!
Create a unity WebGL build and expose the API you need as public methods in a Unity Script you attach to a GameObject.
Then, you should be able to follow the directions at How to call Unity functions from javascript (copied below) on how to call those methods from your javascript code.
You may be able to use UnityScript, which is vaguely similar to JavaScript, to write the Script if you use an old version of Unity. As of this writing, Oculus recommends version 2017.4.11f1, which I think might still support UnityScript.
One major reason you see so much less UnityScript information is that Unity has been moving away from UnityScript, into only supporting C#.
But regardless of if you code your OVRManager script in C# or UnityScript, Unity will make the methods callable from your JavaScript.
Calling Unity scripts functions from JavaScript
Sometimes you need to send some data or notification to the Unity
script from the browser’s JavaScript. The recommended way of doing it
is to call methods on GameObjects in your content. If you are making
the call from a JavaScript plugin, embedded in your project, you can
use the following code:
SendMessage(objectName, methodName, value);
Where objectName is the name of an object in your scene; methodName is
the name of a method in the script, currently attached to that object;
value can be a string, a number, or can be empty. For example:
SendMessage('MyGameObject', 'MyFunction');
SendMessage('MyGameObject', 'MyFunction', 5);
SendMessage('MyGameObject', 'MyFunction', 'MyString');
If you would like to make a call from the global scope of the
embedding page, see the Code Visibility section below.
Code visibility
Starting from Unity 5.6 all the build code is executed in its own
scope. This approach makes it possible to embed your game on an
arbitrary page without causing conflicts with the embedding page code,
as well as makes it possible to embed more than one build on the same
page.
If you have all your JavaScript code in the form of .jslib plugins
inside your project, then this JavaScript code will run inside the
same scope as the compiled build and your code should work pretty much
the same way as in previous versions of Unity (for example, the
following objects and functions should be directly visible from the
JavaScript plugin code: Module, SendMessage, HEAP8, ccall etc.).
However, if you are planning to call the internal JavaScript functions
from the global scope of the embedding page, you should always assume
that there are multiple builds embedded on the page, so you should
explicitly specify which build you are referencing to. For example, if
your game has been instantiated as:
var gameInstance = UnityLoader.instantiate("gameContainer", "Build/build.json", {onProgress: UnityProgress});
Then you can send a message to the build using
gameInstance.SendMessage(), or access the build Module object using
gameInstance.Module.
I'm currently implementing a solution consisting in ADFS 3.0 with multifactor authentication. I've followed the steps in TechNet (https://msdn.microsoft.com/en-GB/Library/dn783423.aspx?f=255&MSPPError=-2147217396) to setup the sample adapter, which works fine.
I now want to extend it and have face a few issues. I've search the web and stackoverflow for something similar but couldn't find anything so I'm posting them all here. Not sure if I need to split this into different questions:
I need to extend the adapter so that part of the html is rendered by an external script, i.e. I need to add an extra script to be loaded when the adfs adapter html is rendered.
From what I could find, one could modify the Theme to include additional javascript in the onload.js, but I don't want to go down this road, since the script really needs to be loaded externally.
The only way I can think of, and kind of works, is to inject javascript in html that loads the script dinamically, something like this:
var script =document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", <my_script_loaded_over_http>)
fileref.onload =
function() {
//call script method
};
But because the external script depends on the "onload" event of the window for some of it's logic, it doesn't work since it's loaded too late..
Is this possible at all?
Is it possible for the adapter to inject new response headers? (I'm assuming it isn't)
Is there a standard way to provide a configuration file to the adapter dll, so that some values can be changed at runtime without having to deploy a new dll? If not, is it safe to write the said configuration file to the adfs folder and read from there?
Thank you very much
I managed to figure this out by myself:
1- The interface IAdapterPresentationForm provides a method to return whatever scripts or css I want to include: string GetFormPreRenderHtml(int lcid) e.g.:
public string GetFormPreRenderHtml(int lcid){
StringBuilder sb = new StringBuilder();
sb.Append("<script src='http://myjs.js' type='text/javascript'></script>");
sb.Append("<link rel='stylesheet' type='text/css' href='http://mycss.css' />");
return sb.ToString();
}
2- Not at all
3- It's possible to pass a configuration file when registering the adapter using:
Register-AdfsAuthenticationProvider -TypeName $typeName -Name -Verbose -ConfigurationFilePath .
The pipeline will open a stream for you, and pass it in:
void OnAuthenticationPipelineLoad(IAuthenticationMethodConfigData configData)
Reference: http://blogs.recneps.net/category/ADFS
What I need is simple: we have a console app project. We want to have such a function that would open a simple window with nothing but html (default system based) html + js rendering window that would read some default html+js string (form string or const char*). we want to have our own api joined with default js api so that when JS calls some our.function(argument); we would have our C++ application performe some function and for example cout argument. How to create such thing on windows?
CoCreate the MSHTML com object for HTML Documents:
CComPtr spDoc;
HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**)&spDoc);
Do something like this to read your HTML string and make the document render it.
Depending on what you need in terms of callbacks, you can use the COM DOM Interfaces to traverse the tree, and then sink the appropriate DispInterfaces to get events on the elements you're interested in. I would recommend this.
If what I suggest in #3 isn't good enough for you (and I'd like to hear why) then you can implement your own ActiveX control and have script on your page call methods on it as suggested by raj.
Brushing aside any security / cross browser/platforms concerns you can use implement an ActiveX object in your C++ that you can invoked from javascript.
http://msdn.microsoft.com/en-us/library/7sw4ddf8(v=vs.94).aspx
The host for the WebBrowser control can provide an object that will be accessible to scripts via the external object.
See http://msdn.microsoft.com/en-us/library/aa770041.aspx#GetExternal
I am trying to create a Silverlight class library which needs to call a few Javascript methods. I have put these methods in a single .js file and have added it to the project. But when I try to call any of the methods in the js file, I get a FailedtoInvoke error. I understand that this problem can be solved by adding a reference of the JS file in the head section of ASPX or HTML page. But I am developing a class library and so that is not possible. Does someone know how to tackle this issue? I am using Silverlight 4 and Visual Studio 2010.
Add the .js file to your library probject as it sounds you have done, make sure it Build Action is "Resource".
You can retrieve this content with Application.GetResourceStream :-
StreamResourceInfo sriJS = Application.GetResourceStream(new Uri("YourAssemblyName;component/MyStuff.js", UriKind.Relative));
Note the structure of the Uri, the assembly name of your class library is needed followed by the literal ";component". This tells Silverlight to look for a resource inside your dll that will be included in a final application's Xap.
Now you need to turn the content of the info's Stream property into a string:-
StreamReader reader = new StreamReader(sriJS.Stream, System.Text.Encoding.UTF8);
string js = reader.ReadToEnd();
Finally you can inject that javascript into the current page with:-
HtmlPage.Window.Eval(js);
Thats it, any global functions defined in the js may now be invoked with HtmlPage.Window.Invoke.
Try something like:
string myScriptCodeString = GetScriptCodeStringFromSomewhere();
System.Windows.Browser.HtmlPage.Window.Eval(myScriptCodeString);
Then the methods defined in myScriptCodeString should be available.