Trick To Get Qt QWebView Bridge Accessible from We

2019-03-01 05:51发布

问题:

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);
  }
}

回答1:

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.