Trick To Get Qt QWebView Bridge Accessible from Webkit IFRAME (Local Pages) - javascript

My Qt C++ (Qt 5.5) application uses a QWebView widget. After a lot of confusion and hard work, I have managed to get the QtWebKit Bridge technique to work and now have my Webkit document (local pages, no web server) to be able to call the C++ functions. The resources for you on that are:
http://doc.qt.io/qt-5/qtwebkit-bridge.html
https://stackoverflow.com/a/4979636/105539
Now, however, I have introduced an IFRAME into the page, and so have a child document. Trouble is, I'm not able to get the child document to connect to that C++ bridge.
Webkit seems to have a whole lot of security controls when doing Javascript communication between child and parent documents when local files are used instead of web servers. All manner of parent and child calling via Javascript failed to work until I stumbled on the complexities of using the postMessage() API of HTML5. At that point, I could get communication established, but it's through messages and not native Javascript function calls.
Is there a technique in Qt where I can establish a bridge between C++ and a document loaded inside an IFRAME inside the QWebView widget, and without using the HTML5 postMessage() API?
Note that one thing I've been able to do is access the IFRAME in code like the following, but the cpp object never appears in Javascript in the iframe, even if I load it from jQuery as $(document).ready(function(){ setTimeout('testIfCPPLoaded();',100); });.
void MainWindow::on_webView_loadFinished(bool arg1)
{
if (arg1 == true) {
QWebFrame *iframe = ui->webView->page()->mainFrame()->findFirstElement("iframe").webFrame();
if (iframe) {
connect(iframe,&QWebFrame::javaScriptWindowObjectCleared,this,&MainWindow::attachJavascript);
}
}
}
void MainWindow::attachJavascript()
{
QWebFrame *frame = ui->webView->page()->mainFrame();
QWebFrame *iframe = ui->webView->page()->mainFrame()->findFirstElement("iframe").webFrame();
frame->addToJavaScriptWindowObject(QString("cpp"), this);
if (iframe) {
iframe->addToJavaScriptWindowObject(QString("cpp"),this);
}
}
void MainWindow::on_webView_urlChanged(const QUrl &arg1)
{
QWebFrame *frame = ui->webView->page()->mainFrame();
QWebFrame *iframe = ui->webView->page()->mainFrame()->findFirstElement("iframe").webFrame();
connect(frame, &QWebFrame::javaScriptWindowObjectCleared, this, &MainWindow::attachJavascript);
if (iframe) {
connect(iframe,&QWebFrame::javaScriptWindowObjectCleared, this, &MainWindow::attachJavascript);
}
}

There are three different techniques for a compromise:
Option A
HTML5 supports a postMessage() API to transfer messages from children IFRAME documents to their parent documents, and it works even with local pages (like with file://). This is a little slower because it's an indirect technique of message passing and interpretation.
Option B
Unlike your Chrome browser with its security controls on pages accessed with file://, Qt's version of WebKit will let you call window.parent.foo() if the parent document contains a function foo(). From there, you can call the cpp object to do tasks. This is slightly indirect, but not as indirect as the postMessage() API.
Option C
If the parent document has an object called cpp (your C++ injected object that you created, as an example), Qt's version of WebKit will let you call var cpp = window.parent.cpp; in order for the IFRAME to have access to the cpp.
One extra suggestion I would have is, if you're testing a WebKit interface in Chrome before being deployed through Qt, you could use an if (window.parent.cpp) (iframe document) and an if (cpp) (parent document) in order to determine if this is loading through Qt or Chrome, and then, if loaded through Chrome, fake a result through a Javascript file (like a kind of polyfill) just so that your Chrome GUI tests work okay until you're ready to hook it up to the C++ in Qt.

Related

Execute javascript without webview in Android

I'm trying to execute a JS fonction in my Android app.
The function is in a .js file on a website.
I'm not using webview, I want to execute the JS function because it sends the request i want.
In the Console in my browser i just have to do question.vote(0);, how can I do it in my app ?
UPDATE 2018: AndroidJSCore has been superseded by LiquidCore, which is based on V8. Not only does it include the V8 engine, but all of Node.js is available as well.
You can execute JavaScript without a WebView. You can use AndroidJSCore. Here is a quick example how you might do it:
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet("http://your_website_here/file.js");
HttpResponse response = client.execute(request);
String js = EntityUtils.toString(response.getEntity());
JSContext context = new JSContext();
context.evaluateScript(js);
context.evaluateScript("question.vote(0);");
However, this most likely won't work outside of a WebView, because I presume you are not only relying on JavaScript, but AJAX, which is not part of pure JavaScript. It requires a browser implementation.
Is there a reason you don't use a hidden WebView and simply inject your code?
// Create a WebView and load a page that includes your JS file
webView.evaluateJavascript("question.vote(0);", null);
For the future reference, there is a library by square for this purpose.
https://github.com/square/duktape-android
This library is a wrapper for Duktape, embeddable JavaScript engine.
You can run javascript without toying with WebView.
Duktape is Ecmascript E5/E5.1 compliant, so basic stuff can be done with this.

Identify requests coming from PageWorker

Is it possible, from within the "http-on-modify-request" event, to identify which requests are coming from a PageWorker object, as opposed to those coming from visible tabs/windows?
Note: Because of redirects and subresources, the URL here is NOT the same URL as the pageWorkers contentURL property.
require("sdk/system/events").on("http-on-modify-request", function(e) {
var httpChannel = e.subject.QueryInterface(Ci.nsIHttpChannel),
url = httpChannel.URI.spec,
origUrl = httpChannel.originalURI.spec;
...
});
I don't know of any way to actually distinguish page-worker requests from "regular" ones.
Current, page workers are implemented like this:
The SDK essentially creates an <iframe> in the hiddenWindow (technically, in sdk/addon/window, which creates a hidden window in the hiddenWindow). The hiddenWindow in mozilla applications is more or less an always-present top-level XUL or HTML window that is simply hidden.
The worker page is loaded into that iframe.
The page-worker will then operate on the DOM on that iframe.
It is possible to identify requests originating from the hidden window and the document within the hidden window.
But identifying if the request or associated document belongs to a page-worker, let alone which page-worker instance, doesn't seem possible, judging from the code. The SDK itself could map the document associated with a request back to a page-worker, as it keeps some WeakMaps around to do so, but that is internal stuff you cannot access.
You only can say that a request is not coming from a page-worker when it is not coming from the hiddenWindow.
Also, keep in mind that there are tons of requests originating neither from a tab nor page-worker: Other (XUL) windows, add-ons, js modules and components, etc...
If it a page-worker created by your add-on that you're interested in: The contentURL property should reflect the final URI once the page is loaded.

From an HTML document, how to determine if it's loaded in a web view

Suppose I have an HTML document, that is available at some URL.
Is it generally possible, for most common cases, to determine whether the documented has been loaded at root level of a web view?
The "most common case" here being a web view of an iOS or Android application. It's enough if I can only determine running inside those. This determination process is expected to be possible from JavaScript (mainly because I don't think any other page content can be useful in this).
I only care about the root level of a web view, meaning that if the page is loaded into a frame within a web view, it's a separate handling case.
No help is expected to be provided by web views, i.e. it's a black box web view.
Here is a mock of what I would like to happen:
<script type="text/javascript">
if (window.contentWindow) {
alert("I'm in an iframe");
} else if (inWebView()) {
alert("I'm in a web view");
} else {
alert("I'm most likely in a web browser");
}
</script>
The check can be reversed - instead of testing for whether the container is a web view, I can test if a container is a well known web browser, but then I will need to check for all of them, desktop included.

UIWebView Javascript Window to Window Communication

I'm working on an iOS app in which I'm trying to use UIWebView to display a variety of websites. Recently I finished logic to inject Javascript into the UIWebView to catch instances of window.open, window.close, and window.opener.focus. In short, to do so, I inject JS that overrides the aforementioned JS functions, which includes creating an iframe with a specific scheme that I can catch in the app's webView:shouldStartLoadWithRequest:navigationType method. This is all working OK for now, including window.open creating a new UIWebView rather than loading in the same window.
Now though, the issue has come up where there's no feasible solution for JS communication between windows. If the child window tries to call to window.opener or window.parent, it's always returning a null value, and thus, it can't communicate back to the original web view.
In an effort to see what iOS browsers are able to effectively perform window-to-window communication, I found that of the 9 browsers I have on my iPhone, only Safari was able to effectively perform this communication successfully. This leads me to believe that there's something with UIWebView that prevents full JS window-to-window communication from being possible.
Has anyone had any success with getting UIWebView to fully integrate with all JS logic, namely window-to-window communication? Or have proof that JS window-to-window communication isn't possible? Any direction or advice is appreciated. Thanks!
Found possible solution.
Add JavaScriptCore.framework to Linked Frameworks and in your webViewDidFinishLoad:
JSContext *parentCtx = [self.parentWebView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
JSContext *childCtx = [self.childWebView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
childCtx[#"window"][#"opener"] = parentCtx[#"window"];
Now when you call window.opener.test() from childWebView, it will fire test function in parentWebView!
I'm not sure about private API.
Works on iOS 7 only
Swift version
import JavaScriptCore
let jsContextA = webA.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext")
let jsContextB = webB.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext")
//Original objc code : jsContextB[#"window"][#"opener"] = jsContextA[#"window"];
jsContextB!.setObject("opener", forKeyedSubscript: "window")
jsContextB!.setObject(jsContextA!.objectForKeyedSubscript("window"), forKeyedSubscript: "opener")

How to call user32.dll methods from javascript

I have a javascript running on a browser. Is it possible to call a function/method in user32.dll.
This is possible from C# by using pInvoke calls. How do I do the same in JavaScript?
Thanks,
Datte
Because of the JavaScript sandbox, you can't do it without a middle layer requiring elevated security permissions, such as a Netscape-style browser plug-in (widely supported), ActiveX control (pretty much IE-only), or .Net control (I assume that's possible; again probably IE-only). In each case, the JavaScript would talk to the control, which would in turn make the USER32 call for you.
None of that will work without the user having granted your application elevated permissions, but I'm guessing as you're requiring Windows, this is for some kind of intranet application where that may be possible.
You definitely need a plug-in, extension or ActiveX of your own installed on the client.
In the case of a firefox extension, you can use jsctypes to wrap the calls easily.
If you use the Jetpack API included with Firefox 4, it will be all JavaScript and won't even require a browser restart.
Here's an exemple from mozilla.org for a basic Hello World :
/* Load JS Ctypes Javascript module */
require("chrome").Cu.import("resource://gre/modules/ctypes.jsm");
/* Load windows api dll */
var lib = ctypes.open("user32.dll");
/* Declare the signature of the function we are going to call */
var msgBox = lib.declare("MessageBoxW",
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.int32_t,
ctypes.ustring,
ctypes.ustring,
ctypes.int32_t);
var MB_OK = 3;
/* Do it! */
var ret = msgBox(0, "Hello world", "title", MB_OK);
/* Display the returned value */
alert("MessageBox result : "+ret);
lib.close();
On the client - it is not possible for security reasons (imagine every site could run system commands on your computer... end of the world - maybe possible with an ActiveX, but that's IE only, but then again, the DLL is windows only).
If you want to run it on the server you'll need to go trough AJAX and C#.
Run dll methods on the client machine using javascript from a web page? That's what is gonna trigger apocalypse.
If you build your own web browser in C#, you can intercept JavaScript calls and translate them to whatever you want in your browser. Though that won't work if you want it to be available to other browsers.
Write a com object that wraps your call to user32. Invoke it in IE/javascript.
Your DynamicWrapperX object would work for this (it would BE that com object, allowing you to just call your dlls as you wish).

Categories

Resources