diff --git a/src/lib/tools/scripts.cpp b/src/lib/tools/scripts.cpp index c466f7ac..13cadc96 100644 --- a/src/lib/tools/scripts.cpp +++ b/src/lib/tools/scripts.cpp @@ -1,334 +1,350 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2015-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "scripts.h" #include "qztools.h" #include "webpage.h" #include #include QString Scripts::setupWebChannel() { QString source = QL1S("(function() {" "%1" "" "function registerExternal(e) {" " window.external = e;" " if (window.external) {" " var event = document.createEvent('Event');" " event.initEvent('_falkon_external_created', true, true);" " window._falkon_external = true;" " document.dispatchEvent(event);" " }" "}" "" "if (self !== top) {" " if (top._falkon_external)" " registerExternal(top.external);" " else" " top.document.addEventListener('_falkon_external_created', function() {" " registerExternal(top.external);" " });" " return;" "}" "" "function registerWebChannel() {" " try {" " new QWebChannel(qt.webChannelTransport, function(channel) {" " var external = channel.objects.qz_object;" " external.extra = {};" " for (var key in channel.objects) {" " if (key != 'qz_object' && key.startsWith('qz_')) {" " external.extra[key.substr(3)] = channel.objects[key];" " }" " }" " registerExternal(external);" " });" " } catch (e) {" " setTimeout(registerWebChannel, 100);" " }" "}" "registerWebChannel();" "" "})()"); return source.arg(QzTools::readAllFileContents(QSL(":/qtwebchannel/qwebchannel.js"))); } QString Scripts::setupFormObserver() { QString source = QL1S("(function() {" "function findUsername(inputs) {" " var usernameNames = ['user', 'name', 'login'];" " for (var i = 0; i < usernameNames.length; ++i) {" " for (var j = 0; j < inputs.length; ++j)" " if (inputs[j].type == 'text' && inputs[j].value.length && inputs[j].name.indexOf(usernameNames[i]) != -1)" " return inputs[j].value;" " }" " for (var i = 0; i < inputs.length; ++i)" " if (inputs[i].type == 'text' && inputs[i].value.length)" " return inputs[i].value;" " for (var i = 0; i < inputs.length; ++i)" " if (inputs[i].type == 'email' && inputs[i].value.length)" " return inputs[i].value;" " return '';" "}" "" "function registerForm(form) {" " form.addEventListener('submit', function() {" " var form = this;" " var data = '';" " var password = '';" " var inputs = form.getElementsByTagName('input');" " for (var i = 0; i < inputs.length; ++i) {" " var input = inputs[i];" " var type = input.type.toLowerCase();" " if (type != 'text' && type != 'password' && type != 'email')" " continue;" " if (!password && type == 'password')" " password = input.value;" " data += encodeURIComponent(input.name);" " data += '=';" " data += encodeURIComponent(input.value);" " data += '&';" " }" " if (!password)" " return;" " data = data.substring(0, data.length - 1);" " var url = window.location.href;" " var username = findUsername(inputs);" " external.autoFill.formSubmitted(url, username, password, data);" " }, true);" "}" "" "if (!document.documentElement) return;" "" "for (var i = 0; i < document.forms.length; ++i)" " registerForm(document.forms[i]);" "" "var observer = new MutationObserver(function(mutations) {" " for (var mutation of mutations)" " for (var node of mutation.addedNodes)" " if (node.tagName && node.tagName.toLowerCase() == 'form')" " registerForm(node);" "});" "observer.observe(document.documentElement, { childList: true, subtree: true });" "" "})()"); return source; } QString Scripts::setupWindowObject() { QString source = QL1S("(function() {" "var external = {};" "external.AddSearchProvider = function(url) {" " window.location = 'falkon:AddSearchProvider?url=' + url;" "};" "external.IsSearchProviderInstalled = function(url) {" " console.warn('NOT IMPLEMENTED: IsSearchProviderInstalled()');" " return false;" "};" "window.external = external;" #if QTWEBENGINEWIDGETS_VERSION < QT_VERSION_CHECK(5, 12, 0) "window.print = function() {" " window.location = 'falkon:PrintPage';" "};" #endif "})()"); return source; } QString Scripts::setupSpeedDial() { QString source = QzTools::readAllFileContents(QSL(":html/speeddial.user.js")); source.replace(QL1S("%JQUERY%"), QzTools::readAllFileContents(QSL(":html/jquery.js"))); source.replace(QL1S("%JQUERY-UI%"), QzTools::readAllFileContents(QSL(":html/jquery-ui.js"))); return source; } QString Scripts::setCss(const QString &css) { QString source = QL1S("(function() {" "var head = document.getElementsByTagName('head')[0];" "if (!head) return;" "var css = document.createElement('style');" "css.setAttribute('type', 'text/css');" "css.appendChild(document.createTextNode('%1'));" "head.appendChild(css);" "})()"); QString style = css; style.replace(QL1S("'"), QL1S("\\'")); style.replace(QL1S("\n"), QL1S("\\n")); return source.arg(style); } QString Scripts::sendPostData(const QUrl &url, const QByteArray &data) { QString source = QL1S("(function() {" "var form = document.createElement('form');" "form.setAttribute('method', 'POST');" "form.setAttribute('action', '%1');" "var val;" "%2" "form.submit();" "})()"); QString valueSource = QL1S("val = document.createElement('input');" "val.setAttribute('type', 'hidden');" "val.setAttribute('name', '%1');" "val.setAttribute('value', '%2');" "form.appendChild(val);"); QString values; QUrlQuery query(data); const auto &queryItems = query.queryItems(QUrl::FullyDecoded); for (int i = 0; i < queryItems.size(); ++i) { const auto &pair = queryItems[i]; QString value = pair.first; QString key = pair.second; value.replace(QL1S("'"), QL1S("\\'")); key.replace(QL1S("'"), QL1S("\\'")); values.append(valueSource.arg(value, key)); } return source.arg(url.toString(), values); } QString Scripts::completeFormData(const QByteArray &data) { QString source = QL1S("(function() {" "var data = '%1'.split('&');" "var inputs = document.getElementsByTagName('input');" "" "for (var i = 0; i < data.length; ++i) {" " var pair = data[i].split('=');" " if (pair.length != 2)" " continue;" " var key = decodeURIComponent(pair[0]);" " var val = decodeURIComponent(pair[1]);" " for (var j = 0; j < inputs.length; ++j) {" " var input = inputs[j];" " var type = input.type.toLowerCase();" " if (type != 'text' && type != 'password' && type != 'email')" " continue;" " if (input.name == key) {" " input.value = val;" " input.dispatchEvent(new Event('change'));" " }" " }" "}" "" "})()"); QString d = data; d.replace(QL1S("'"), QL1S("\\'")); return source.arg(d); } QString Scripts::getOpenSearchLinks() { QString source = QL1S("(function() {" "var out = [];" "var links = document.getElementsByTagName('link');" "for (var i = 0; i < links.length; ++i) {" " var e = links[i];" " if (e.type == 'application/opensearchdescription+xml') {" " out.push({" " url: e.href," " title: e.title" " });" " }" "}" "return out;" "})()"); return source; } QString Scripts::getAllImages() { QString source = QL1S("(function() {" "var out = [];" "var imgs = document.getElementsByTagName('img');" "for (var i = 0; i < imgs.length; ++i) {" " var e = imgs[i];" " out.push({" " src: e.src," " alt: e.alt" " });" "}" "return out;" "})()"); return source; } QString Scripts::getAllMetaAttributes() { QString source = QL1S("(function() {" "var out = [];" "var meta = document.getElementsByTagName('meta');" "for (var i = 0; i < meta.length; ++i) {" " var e = meta[i];" " out.push({" " name: e.getAttribute('name')," " content: e.getAttribute('content')," " httpequiv: e.getAttribute('http-equiv')" " });" "}" "return out;" "})()"); return source; } QString Scripts::getFormData(const QPointF &pos) { QString source = QL1S("(function() {" "var e = document.elementFromPoint(%1, %2);" "if (!e || e.tagName.toLowerCase() != 'input')" " return;" "var fe = e.parentElement;" "while (fe) {" " if (fe.tagName.toLowerCase() == 'form')" " break;" " fe = fe.parentElement;" "}" "if (!fe)" " return;" "var res = {" " method: fe.method.toLowerCase()," " action: fe.action," " inputName: e.name," " inputs: []," "};" "for (var i = 0; i < fe.length; ++i) {" " var input = fe.elements[i];" " res.inputs.push([input.name, input.value]);" "}" "return res;" "})()"); return source.arg(pos.x()).arg(pos.y()); } + +QString Scripts::scrollToAnchor(const QString &anchor) +{ + QString source = QL1S("(function() {" + "var e = document.getElementById(\"%1\");" + "if (!e) {" + " var els = document.querySelectorAll(\"[name='%1']\");" + " if (els.length)" + " e = els[0];" + "}" + "if (e)" + " e.scrollIntoView();" + "})()"); + + return source.arg(anchor); +} diff --git a/src/lib/tools/scripts.h b/src/lib/tools/scripts.h index 0d34f768..f2b3bd25 100644 --- a/src/lib/tools/scripts.h +++ b/src/lib/tools/scripts.h @@ -1,45 +1,46 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2015-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef SCRIPTS_H #define SCRIPTS_H #include #include "qzcommon.h" class QWebEngineView; class FALKON_EXPORT Scripts { public: static QString setupWebChannel(); static QString setupFormObserver(); static QString setupWindowObject(); static QString setupSpeedDial(); static QString setCss(const QString &css); static QString sendPostData(const QUrl &url, const QByteArray &data); static QString completeFormData(const QByteArray &data); static QString getOpenSearchLinks(); static QString getAllImages(); static QString getAllMetaAttributes(); static QString getFormData(const QPointF &pos); + static QString scrollToAnchor(const QString &anchor); }; #endif // SCRIPTS_H diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp index 8e2f536f..54cb7257 100644 --- a/src/lib/webengine/webpage.cpp +++ b/src/lib/webengine/webpage.cpp @@ -1,718 +1,718 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "webpage.h" #include "tabbedwebview.h" #include "browserwindow.h" #include "pluginproxy.h" #include "downloadmanager.h" #include "mainapplication.h" #include "checkboxdialog.h" #include "qztools.h" #include "speeddial.h" #include "autofill.h" #include "popupwebview.h" #include "popupwindow.h" #include "iconprovider.h" #include "qzsettings.h" #include "useragentmanager.h" #include "delayedfilewatcher.h" #include "searchenginesmanager.h" #include "html5permissions/html5permissionsmanager.h" #include "javascript/externaljsobject.h" #include "tabwidget.h" #include "networkmanager.h" #include "webhittestresult.h" #include "ui_jsconfirm.h" #include "ui_jsalert.h" #include "ui_jsprompt.h" #include "passwordmanager.h" +#include "scripts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include QString WebPage::s_lastUploadLocation = QDir::homePath(); QUrl WebPage::s_lastUnsupportedUrl; QTime WebPage::s_lastUnsupportedUrlTime; QStringList s_supportedSchemes; static const bool kEnableJsOutput = qEnvironmentVariableIsSet("FALKON_ENABLE_JS_OUTPUT"); static const bool kEnableJsNonBlockDialogs = qEnvironmentVariableIsSet("FALKON_ENABLE_JS_NONBLOCK_DIALOGS"); WebPage::WebPage(QObject* parent) : QWebEnginePage(mApp->webProfile(), parent) , m_fileWatcher(0) , m_runningLoop(0) , m_loadProgress(100) , m_blockAlerts(false) , m_secureStatus(false) { QWebChannel *channel = new QWebChannel(this); ExternalJsObject::setupWebChannel(channel, this); setWebChannel(channel, SafeJsWorld); connect(this, &QWebEnginePage::loadProgress, this, &WebPage::progress); connect(this, &QWebEnginePage::loadFinished, this, &WebPage::finished); connect(this, &QWebEnginePage::urlChanged, this, &WebPage::urlChanged); connect(this, &QWebEnginePage::featurePermissionRequested, this, &WebPage::featurePermissionRequested); connect(this, &QWebEnginePage::windowCloseRequested, this, &WebPage::windowCloseRequested); connect(this, &QWebEnginePage::fullScreenRequested, this, &WebPage::fullScreenRequested); connect(this, &QWebEnginePage::renderProcessTerminated, this, &WebPage::renderProcessTerminated); connect(this, &QWebEnginePage::authenticationRequired, this, [this](const QUrl &url, QAuthenticator *auth) { mApp->networkManager()->authentication(url, auth, view()); }); connect(this, &QWebEnginePage::proxyAuthenticationRequired, this, [this](const QUrl &, QAuthenticator *auth, const QString &proxyHost) { mApp->networkManager()->proxyAuthentication(proxyHost, auth, view()); }); // Workaround QWebEnginePage not scrolling to anchors when opened in background tab m_contentsResizedConnection = connect(this, &QWebEnginePage::contentsSizeChanged, this, [this]() { const QString fragment = url().fragment(); if (!fragment.isEmpty()) { - const QString src = QSL("var els = document.querySelectorAll(\"[name='%1']\"); if (els.length) els[0].scrollIntoView();"); - runJavaScript(src.arg(fragment)); + runJavaScript(Scripts::scrollToAnchor(fragment)); } disconnect(m_contentsResizedConnection); }); // Workaround for broken load started/finished signals in QtWebEngine 5.10, 5.11 connect(this, &QWebEnginePage::loadProgress, this, [this](int progress) { if (progress == 100) { emit loadFinished(true); } }); #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 12, 0) connect(this, &QWebEnginePage::printRequested, this, &WebPage::printRequested); connect(this, &QWebEnginePage::selectClientCertificate, this, [this](QWebEngineClientCertificateSelection selection) { // TODO: It should prompt user selection.select(selection.certificates().at(0)); }); #endif } WebPage::~WebPage() { if (m_runningLoop) { m_runningLoop->exit(1); m_runningLoop = 0; } } WebView *WebPage::view() const { return static_cast(QWebEnginePage::view()); } bool WebPage::execPrintPage(QPrinter *printer, int timeout) { QPointer loop = new QEventLoop; bool result = false; QTimer::singleShot(timeout, loop.data(), &QEventLoop::quit); print(printer, [loop, &result](bool res) { if (loop && loop->isRunning()) { result = res; loop->quit(); } }); loop->exec(); delete loop; return result; } QVariant WebPage::execJavaScript(const QString &scriptSource, quint32 worldId, int timeout) { QPointer loop = new QEventLoop; QVariant result; QTimer::singleShot(timeout, loop.data(), &QEventLoop::quit); runJavaScript(scriptSource, worldId, [loop, &result](const QVariant &res) { if (loop && loop->isRunning()) { result = res; loop->quit(); } }); loop->exec(QEventLoop::ExcludeUserInputEvents); delete loop; return result; } QPointF WebPage::mapToViewport(const QPointF &pos) const { return QPointF(pos.x() / zoomFactor(), pos.y() / zoomFactor()); } WebHitTestResult WebPage::hitTestContent(const QPoint &pos) const { return WebHitTestResult(this, pos); } void WebPage::scroll(int x, int y) { runJavaScript(QSL("window.scrollTo(window.scrollX + %1, window.scrollY + %2)").arg(x).arg(y), SafeJsWorld); } void WebPage::setScrollPosition(const QPointF &pos) { const QPointF v = mapToViewport(pos.toPoint()); runJavaScript(QSL("window.scrollTo(%1, %2)").arg(v.x()).arg(v.y()), SafeJsWorld); } bool WebPage::isRunningLoop() { return m_runningLoop; } bool WebPage::isLoading() const { return m_loadProgress < 100; } // static QStringList WebPage::internalSchemes() { return QStringList{ QSL("http"), QSL("https"), QSL("file"), QSL("ftp"), QSL("data"), QSL("about"), QSL("view-source"), QSL("chrome") }; } // static QStringList WebPage::supportedSchemes() { if (s_supportedSchemes.isEmpty()) { s_supportedSchemes = internalSchemes(); } return s_supportedSchemes; } // static void WebPage::addSupportedScheme(const QString &scheme) { s_supportedSchemes = supportedSchemes(); if (!s_supportedSchemes.contains(scheme)) { s_supportedSchemes.append(scheme); } } // static void WebPage::removeSupportedScheme(const QString &scheme) { s_supportedSchemes.removeOne(scheme); } void WebPage::urlChanged(const QUrl &url) { Q_UNUSED(url) if (isLoading()) { m_blockAlerts = false; } } void WebPage::progress(int prog) { m_loadProgress = prog; bool secStatus = url().scheme() == QL1S("https"); if (secStatus != m_secureStatus) { m_secureStatus = secStatus; emit privacyChanged(secStatus); } } void WebPage::finished() { progress(100); // File scheme watcher if (url().scheme() == QLatin1String("file")) { QFileInfo info(url().toLocalFile()); if (info.isFile()) { if (!m_fileWatcher) { m_fileWatcher = new DelayedFileWatcher(this); connect(m_fileWatcher, &DelayedFileWatcher::delayedFileChanged, this, &WebPage::watchedFileChanged); } const QString filePath = url().toLocalFile(); if (QFile::exists(filePath) && !m_fileWatcher->files().contains(filePath)) { m_fileWatcher->addPath(filePath); } } } else if (m_fileWatcher && !m_fileWatcher->files().isEmpty()) { m_fileWatcher->removePaths(m_fileWatcher->files()); } // AutoFill m_autoFillUsernames = mApp->autoFill()->completePage(this, url()); } void WebPage::watchedFileChanged(const QString &file) { if (url().toLocalFile() == file) { triggerAction(QWebEnginePage::Reload); } } void WebPage::handleUnknownProtocol(const QUrl &url) { const QString protocol = url.scheme(); if (protocol == QLatin1String("mailto")) { desktopServicesOpen(url); return; } if (qzSettings->blockedProtocols.contains(protocol)) { qDebug() << "WebPage::handleUnknownProtocol Protocol" << protocol << "is blocked!"; return; } if (qzSettings->autoOpenProtocols.contains(protocol)) { desktopServicesOpen(url); return; } CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, view()); dialog.setDefaultButton(QMessageBox::Yes); const QString wrappedUrl = QzTools::alignTextToWidth(url.toString(), "
", dialog.fontMetrics(), 450); const QString text = tr("Falkon cannot handle %1: links. The requested link " "is
  • %2
Do you want Falkon to try " "open this link in system application?").arg(protocol, wrappedUrl); dialog.setText(text); dialog.setCheckBoxText(tr("Remember my choice for this protocol")); dialog.setWindowTitle(tr("External Protocol Request")); dialog.setIcon(QMessageBox::Question); switch (dialog.exec()) { case QMessageBox::Yes: if (dialog.isChecked()) { qzSettings->autoOpenProtocols.append(protocol); qzSettings->saveSettings(); } QDesktopServices::openUrl(url); break; case QMessageBox::No: if (dialog.isChecked()) { qzSettings->blockedProtocols.append(protocol); qzSettings->saveSettings(); } break; default: break; } } void WebPage::desktopServicesOpen(const QUrl &url) { // Open same url only once in 2 secs const int sameUrlTimeout = 2 * 1000; if (s_lastUnsupportedUrl != url || s_lastUnsupportedUrlTime.isNull() || s_lastUnsupportedUrlTime.elapsed() > sameUrlTimeout) { s_lastUnsupportedUrl = url; s_lastUnsupportedUrlTime.restart(); QDesktopServices::openUrl(url); } else { qWarning() << "WebPage::desktopServicesOpen Url" << url << "has already been opened!\n" "Ignoring it to prevent infinite loop!"; } } void WebPage::windowCloseRequested() { if (!view()) return; view()->closeView(); } void WebPage::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) { view()->requestFullScreen(fullScreenRequest.toggleOn()); const bool accepted = fullScreenRequest.toggleOn() == view()->isFullScreen(); if (accepted) fullScreenRequest.accept(); else fullScreenRequest.reject(); } void WebPage::featurePermissionRequested(const QUrl &origin, const QWebEnginePage::Feature &feature) { if (feature == MouseLock && view()->isFullScreen()) setFeaturePermission(origin, feature, PermissionGrantedByUser); else mApp->html5PermissionsManager()->requestPermissions(this, origin, feature); } void WebPage::renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) { Q_UNUSED(exitCode) if (terminationStatus == NormalTerminationStatus) return; QTimer::singleShot(0, this, [this]() { QString page = QzTools::readAllFileContents(":html/tabcrash.html"); page.replace(QL1S("%IMAGE%"), QzTools::pixmapToDataUrl(IconProvider::standardIcon(QStyle::SP_MessageBoxWarning).pixmap(45)).toString()); page.replace(QL1S("%TITLE%"), tr("Failed loading page")); page.replace(QL1S("%HEADING%"), tr("Failed loading page")); page.replace(QL1S("%LI-1%"), tr("Something went wrong while loading this page.")); page.replace(QL1S("%LI-2%"), tr("Try reloading the page or closing some tabs to make more memory available.")); page.replace(QL1S("%RELOAD-PAGE%"), tr("Reload page")); page = QzTools::applyDirectionToPage(page); setHtml(page.toUtf8(), url()); }); } bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) { if (mApp->isClosing()) { return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); } if (!mApp->plugins()->acceptNavigationRequest(this, url, type, isMainFrame)) return false; if (url.scheme() == QL1S("falkon")) { if (url.path() == QL1S("AddSearchProvider")) { QUrlQuery query(url); mApp->searchEnginesManager()->addEngine(QUrl(query.queryItemValue(QSL("url")))); return false; #if QTWEBENGINEWIDGETS_VERSION < QT_VERSION_CHECK(5, 12, 0) } else if (url.path() == QL1S("PrintPage")) { emit printRequested(); return false; #endif } } const bool result = QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); if (result) { if (isMainFrame) { const bool isWeb = url.scheme() == QL1S("http") || url.scheme() == QL1S("https") || url.scheme() == QL1S("file"); const bool globalJsEnabled = mApp->webSettings()->testAttribute(QWebEngineSettings::JavascriptEnabled); settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, isWeb ? globalJsEnabled : true); } emit navigationRequestAccepted(url, type, isMainFrame); } return result; } bool WebPage::certificateError(const QWebEngineCertificateError &error) { return mApp->networkManager()->certificateError(error, view()); } QStringList WebPage::chooseFiles(QWebEnginePage::FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) { Q_UNUSED(acceptedMimeTypes); QStringList files; QString suggestedFileName = s_lastUploadLocation; if (!oldFiles.isEmpty()) suggestedFileName = oldFiles.at(0); switch (mode) { case FileSelectOpen: files = QStringList(QzTools::getOpenFileName("WebPage-ChooseFile", view(), tr("Choose file..."), suggestedFileName)); break; case FileSelectOpenMultiple: files = QzTools::getOpenFileNames("WebPage-ChooseFile", view(), tr("Choose files..."), suggestedFileName); break; default: files = QWebEnginePage::chooseFiles(mode, oldFiles, acceptedMimeTypes); break; } if (!files.isEmpty()) s_lastUploadLocation = files.at(0); return files; } QStringList WebPage::autoFillUsernames() const { return m_autoFillUsernames; } bool WebPage::javaScriptPrompt(const QUrl &securityOrigin, const QString &msg, const QString &defaultValue, QString* result) { if (!kEnableJsNonBlockDialogs) { return QWebEnginePage::javaScriptPrompt(securityOrigin, msg, defaultValue, result); } if (m_runningLoop) { return false; } QFrame *widget = new QFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsPrompt* ui = new Ui_jsPrompt(); ui->setupUi(widget); ui->message->setText(msg); ui->lineEdit->setText(defaultValue); ui->lineEdit->setFocus(); widget->resize(view()->size()); widget->show(); QAbstractButton *clicked = nullptr; connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [&](QAbstractButton *button) { clicked = button; }); connect(view(), &WebView::viewportResized, widget, QOverload::of(&QFrame::resize)); connect(ui->lineEdit, SIGNAL(returnPressed()), ui->buttonBox->button(QDialogButtonBox::Ok), SLOT(animateClick())); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, &QDialogButtonBox::clicked, &eLoop, &QEventLoop::quit); if (eLoop.exec() == 1) { return result; } m_runningLoop = 0; QString x = ui->lineEdit->text(); bool _result = ui->buttonBox->buttonRole(clicked) == QDialogButtonBox::AcceptRole; *result = x; delete widget; view()->setFocus(); return _result; } bool WebPage::javaScriptConfirm(const QUrl &securityOrigin, const QString &msg) { if (!kEnableJsNonBlockDialogs) { return QWebEnginePage::javaScriptConfirm(securityOrigin, msg); } if (m_runningLoop) { return false; } QFrame *widget = new QFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsConfirm* ui = new Ui_jsConfirm(); ui->setupUi(widget); ui->message->setText(msg); ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); widget->resize(view()->size()); widget->show(); QAbstractButton *clicked = nullptr; connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [&](QAbstractButton *button) { clicked = button; }); connect(view(), &WebView::viewportResized, widget, QOverload::of(&QFrame::resize)); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, &QDialogButtonBox::clicked, &eLoop, &QEventLoop::quit); if (eLoop.exec() == 1) { return false; } m_runningLoop = 0; bool result = ui->buttonBox->buttonRole(clicked) == QDialogButtonBox::AcceptRole; delete widget; view()->setFocus(); return result; } void WebPage::javaScriptAlert(const QUrl &securityOrigin, const QString &msg) { Q_UNUSED(securityOrigin) if (m_blockAlerts || m_runningLoop) { return; } if (!kEnableJsNonBlockDialogs) { QString title = tr("JavaScript alert"); if (!url().host().isEmpty()) { title.append(QString(" - %1").arg(url().host())); } CheckBoxDialog dialog(QMessageBox::Ok, view()); dialog.setDefaultButton(QMessageBox::Ok); dialog.setWindowTitle(title); dialog.setText(msg); dialog.setCheckBoxText(tr("Prevent this page from creating additional dialogs")); dialog.setIcon(QMessageBox::Information); dialog.exec(); m_blockAlerts = dialog.isChecked(); return; } QFrame *widget = new QFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsAlert* ui = new Ui_jsAlert(); ui->setupUi(widget); ui->message->setText(msg); ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); widget->resize(view()->size()); widget->show(); connect(view(), &WebView::viewportResized, widget, QOverload::of(&QFrame::resize)); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, &QDialogButtonBox::clicked, &eLoop, &QEventLoop::quit); if (eLoop.exec() == 1) { return; } m_runningLoop = 0; m_blockAlerts = ui->preventAlerts->isChecked(); delete widget; view()->setFocus(); } void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) { if (!kEnableJsOutput) { return; } switch (level) { case InfoMessageLevel: std::cout << "[I] "; break; case WarningMessageLevel: std::cout << "[W] "; break; case ErrorMessageLevel: std::cout << "[E] "; break; } std::cout << qPrintable(sourceID) << ":" << lineNumber << " " << qPrintable(message); } QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type) { TabbedWebView *tView = qobject_cast(view()); BrowserWindow *window = tView ? tView->browserWindow() : mApp->getWindow(); auto createTab = [=](Qz::NewTabPositionFlags pos) { int index = window->tabWidget()->addView(QUrl(), pos); TabbedWebView* view = window->weView(index); view->setPage(new WebPage); if (tView) { tView->webTab()->addChildTab(view->webTab()); } // Workaround focus issue when creating tab if (pos.testFlag(Qz::NT_SelectedTab)) { QPointer pview = view; pview->setFocus(); QTimer::singleShot(100, this, [pview]() { if (pview && pview->webTab()->isCurrentTab()) { pview->setFocus(); } }); } return view->page(); }; switch (type) { case QWebEnginePage::WebBrowserWindow: { BrowserWindow *window = mApp->createWindow(Qz::BW_NewWindow); WebPage *page = new WebPage; window->setStartPage(page); return page; } case QWebEnginePage::WebDialog: if (!qzSettings->openPopupsInTabs) { PopupWebView* view = new PopupWebView; view->setPage(new WebPage); PopupWindow* popup = new PopupWindow(view); popup->show(); window->addDeleteOnCloseWidget(popup); return view->page(); } // else fallthrough case QWebEnginePage::WebBrowserTab: return createTab(Qz::NT_CleanSelectedTab); case QWebEnginePage::WebBrowserBackgroundTab: return createTab(Qz::NT_CleanNotSelectedTab); default: break; } return Q_NULLPTR; }