QWebFrame::evaluateJavaScript crash
-
Hi all,
I'm getting random crashes when I'm trying to run a rather large Javascript script via QWebFrame::evaluateJavaScript(). Is this a bug in webkit, Qt, or could I be doing something wrong?
Basically, what I'm doing is:
@QWebPage webPage;
QWebFrame* webFrame = webPage.mainFrame();
webFrame->setContent(QString("<script type="text/javascript">" + scriptContent + "</script>").toUtf8());
// I tried webFrame->evaluateJavaScript(scriptContent) with the same "result"
QVariant callResult = webFrame->evaluateJavaScript(functionCall);@Stack trace:
@Thread 3 Crashed:: Thread (pooled)
0 QtWebKit 0x000000010176e8e1 WTFCrash + 113
1 QtWebKit 0x0000000101795aa3 WTF::StringImpl::~StringImpl() + 339
2 QtWebKit 0x0000000101795abe WTF::StringImpl::destroy(WTF::StringImpl*) + 14
3 QtWebKit 0x00000001014ee307 JSC::MarkedBlock::FreeList JSC::MarkedBlock::specializedSweep<(JSC::MarkedBlock::BlockState)3, (JSC::MarkedBlock::SweepMode)1, (JSC::MarkedBlock::DestructorType)1>() + 167
4 QtWebKit 0x00000001014edd14 JSC::MarkedBlock::FreeList JSC::MarkedBlock::sweepHelper<(JSC::MarkedBlock::DestructorType)1>(JSC::MarkedBlock::SweepMode) + 164
5 QtWebKit 0x00000001014edb59 JSC::MarkedBlock::sweep(JSC::MarkedBlock::SweepMode) + 73
6 QtWebKit 0x00000001014ecd1a JSC::MarkedAllocator::allocateSlowCase(unsigned long) + 90
7 QtWebKit 0x000000010069a003 JSC::JSString::create(JSC::VM&, WTF::PassRefPtrWTF::StringImpl) + 243
8 QtWebKit 0x0000000100699e7d JSC::jsString(JSC::ExecState*, WTF::String const&) + 157
9 QtWebKit 0x00000001016e0b98 JSC::JSFunction::finishCreation(JSC::ExecState*, JSC::NativeExecutable*, int, WTF::String const&) + 88
10 QtWebKit 0x00000001016e0ad5 JSC::JSFunction::create(JSC::ExecState*, JSC::JSGlobalObject*, int, WTF::String const&, long long ()(JSC::ExecState), JSC::Intrinsic, long long ()(JSC::ExecState)) + 229
11 QtWebKit 0x00000001016cfd03 JSC::FunctionPrototype::addFunctionProperties(JSC::ExecState*, JSC::JSGlobalObject*, JSC::JSFunction**, JSC::JSFunction**) + 467
12 QtWebKit 0x00000001016e9e76 JSC::JSGlobalObject::reset(JSC::JSValue) + 838
13 QtWebKit 0x00000001013b17ea WebCore::JSDOMWindowBase::finishCreation(JSC::VM&, WebCore::JSDOMWindowShell*) + 202
14 QtWebKit 0x00000001013d83e1 WebCore::JSDOMWindow::create(JSC::VM&, JSC::Structure*, WTF::PassRefPtrWebCore::DOMWindow, WebCore::JSDOMWindowShell*) + 161
15 QtWebKit 0x00000001013b6530 WebCore::JSDOMWindowShell::setWindow(WTF::PassRefPtrWebCore::DOMWindow) + 352
16 QtWebKit 0x00000001013b639f WebCore::JSDOMWindowShell::finishCreation(JSC::VM&, WTF::PassRefPtrWebCore::DOMWindow) + 31
17 QtWebKit 0x000000010068e2a8 WebCore::JSDOMWindowShell::create(WTF::PassRefPtrWebCore::DOMWindow, JSC::Structure*, WebCore::DOMWrapperWorld*) + 104
18 QtWebKit 0x000000010068c748 WebCore::ScriptController::createWindowShell(WebCore::DOMWrapperWorld*) + 168
19 QtWebKit 0x000000010068cf43 WebCore::ScriptController::initScript(WebCore::DOMWrapperWorld*) + 51
20 QtWebKit 0x000000010068c94f WebCore::ScriptController::evaluateInWorld(WebCore::ScriptSourceCode const&, WebCore::DOMWrapperWorld*) + 159
21 QtWebKit 0x000000010068cb89 WebCore::ScriptController::evaluate(WebCore::ScriptSourceCode const&) + 41
22 QtWebKit 0x0000000100687ae3 WebCore::ScriptController::executeScript(WebCore::ScriptSourceCode const&) + 99
23 QtWebKit 0x00000001005265d3 QWebFrameAdapter::evaluateJavaScript(QString const&) + 163
24 QtWebKitWidgets 0x000000010049a2c2 QWebFrame::evaluateJavaScript(QString const&) + 18
25 com.tubulatorapp 0x000000010011f2d0 ScriptInterface::runJSInDom(QString, QString) + 784 (ScriptInterface.cpp:54)@Any clues?
-
Alrightie then. Since my case is rather specific (I need to evaluate a function call in a 3rd party JS chunk, and don't really care about the DOM as such - I just need the code to run), I ended up using QScriptEngine instead with a very sparse dummy DOM injected in the script engine instance, and then running the desired method.
Not sure what the issue with QWebKit is here (threading?), but QScriptEngine works for me in this case.
Others having the same issue (but needing a full DOM) might be interested in http://www.envjs.com, implementing a full DOM. The project https://qt.gitorious.org/qt-labs/qtscript-browser-env puts this in a Qt context as a QScriptEngine plugin, although the project is not compiling for Qt5.x. Should be easy to fix though.
-
occurs only in debug mode?
-
I think the
QWebFrame::setContent
does not work well, it is equivalent tosetHTML
but is a little more advanced, so has no way to set the content of a page has loaded.The right thing would be this:
QWebPage *page = ui->webview->page(); QString scriptContent = "<scr" + "ipt>function HelloWorld() { alert('Hello World!!!'); }</sc" + "ript>"; QByteArray injectScript = QString(scriptContent).toUtf8(); page->mainFrame()->setContent(injectScript, "text/html");@
But for your purpose this will not work, the right would you use another type of injection, such as the very "evaluateScript", like this:
QString injectionJS = "(function() { var new = document.createElement('scr" + "ipt');"; injectionJS += "new.inner" + "HTML = String((" + scriptContent + "));"; injectionJS += "document.body.appendChild(new)"; injectionJS += "});"; page->mainFrame()->evaluateJavaScript(injectionJS);
It's true
Sounds like a bug. Please go to http://bugreports.qt-project.org/
After create an issue, post your link here.Thanks!
-
Not the evaluateScript that causes the "crash", but using the incorrect way QwebFrame::setContent. He apparently simulates the loading of a file, but if the page is already loaded it fails to load again.
Same is not true-if the "crash" the QwebFrame::setContent would not work for what you want to do, because it would wipe the entire page and its original content would be lost.
View this would be the proper way to work with QwebFrame::setContent does:
@QwebFrame::setContent (QString("<scr"+"ipt>content</scr"+"ipt>").toUtf8()," text/html");@
What I mean is that it seems that server to create a home page (as in Chrome have the "SpeedDial")
For you to achieve your goal, I recommend doing something like this:
https://gist.github.com/brcontainer/98016ee3655cfa535e8e (I fixed my example here)
I tested, no crash using evaluateScript.
-
Hm, the thing is, my approach works in most cases. The JS gets loaded and run. But when it meets certain injections (they are LARGE all of them - several MB of minified JS code), it crashes (as in the application dies, with a crash report and all, as in the one I've shown).
As you can see in the crash report, the culprit has to do with a string destructor after my call to QWebFrame::evaluateJavaScript(). For some reason I don't think it's related to the way the JS is loaded.
In any case, I'll try your approach at the earliest convenience; but for now QScript works for me.Thanks!
-
Got it, then it is a limitation of reading time with the QtWebKit runtime.
Perhaps the process for files only work if the injection was asynchronous, but the QtWebKit does not support multiprocess (as is Chrome).
Why do not you do this:
-
Create um custom protocol in "QNetWorkAccessManager::createRequest", like "qrc:", eg. @janfaore://@
-
Save your Javascript in an file located in "User folder" (in Window c:\Users\USER_NAME\Local Datac\AppData\Local\project_user_data)
-
After save, inject saved file by your custom protocol, like this: @janfaore://myfile.js@
-
Set an interval/timeout before perform: @QWebFrame::evaluateScript("custom_function();");@
QNetWorkAccessManager example:
@QNetworkReply * netWork::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) {
const QString scheme = request.url().scheme().toLower().trimmed();
if ("janfaore" == scheme) {
return [YOUR CUSTOM QNetworkReply];
}QNetWorkAccessManager::createRequest(op, request, outgoingData);
}@
Note: Maybe need reimplement QNetWorkReply (create an custom QNetWorkReply).
-
-
I appreciate your efforts in trying to help, but the problem is not loading the scripts (which are online resources that change regularly). I have no problem with this being asynchronous. The function I need to call via evaluateScript() is very, very simple and short (a few string manipulations), and a timeout in that context is very inlikely.
-
Is that you said that major routes injected with "the evaluate" that caused the "crash", by this thought that if you create yourself a custom protocol like in "Firefox", you could throw huge scripts and the "Main Thread" (windows) would not crash.
Using "evaluateScript", you inject only this:
@< script src="janfaore://myfile.js"></ script >@
After use QTimer::singleShot() for evaluate your function.