mirror of
https://invent.kde.org/network/falkon.git
synced 2024-12-19 18:26:34 +01:00
AutoFill: Initial port to QtWebEngine
There are still issues with completing forms in frames
This commit is contained in:
parent
a04efdc0be
commit
32a0db31b3
@ -24,7 +24,6 @@
|
||||
#include "mainapplication.h"
|
||||
#include "webpage.h"
|
||||
#include "qztools.h"
|
||||
#include "networkmanager.h"
|
||||
#include "browserwindow.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "bookmarkstoolbar.h"
|
||||
#include "clearprivatedata.h"
|
||||
#include "sourceviewer.h"
|
||||
#include "networkmanager.h"
|
||||
#include "autofill.h"
|
||||
#include "networkmanagerproxy.h"
|
||||
#include "mainapplication.h"
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "pluginproxy.h"
|
||||
#include "iconprovider.h"
|
||||
#include "browserwindow.h"
|
||||
#include "networkmanager.h"
|
||||
#include "checkboxdialog.h"
|
||||
#include "profilemanager.h"
|
||||
#include "adblockmanager.h"
|
||||
@ -268,6 +267,7 @@ MainApplication::MainApplication(int &argc, char** argv)
|
||||
loadSettings();
|
||||
|
||||
m_plugins = new PluginProxy;
|
||||
m_autoFill = new AutoFill(this);
|
||||
|
||||
if (!noAddons)
|
||||
m_plugins->loadPlugins();
|
||||
@ -503,9 +503,6 @@ Bookmarks* MainApplication::bookmarks()
|
||||
|
||||
AutoFill* MainApplication::autoFill()
|
||||
{
|
||||
if (!m_autoFill) {
|
||||
m_autoFill = new AutoFill(this);
|
||||
}
|
||||
return m_autoFill;
|
||||
}
|
||||
|
||||
|
@ -23,14 +23,16 @@
|
||||
#include "popupwebview.h"
|
||||
#include "mainapplication.h"
|
||||
#include "autofillnotification.h"
|
||||
#include "pageformcompleter.h"
|
||||
#include "settings.h"
|
||||
#include "passwordmanager.h"
|
||||
#include "qztools.h"
|
||||
#include "scripts.h"
|
||||
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QNetworkRequest>
|
||||
#include <QWebEngineProfile>
|
||||
#include <QWebEngineScriptCollection>
|
||||
|
||||
#include <QUrlQuery>
|
||||
|
||||
@ -40,6 +42,15 @@ AutoFill::AutoFill(QObject* parent)
|
||||
, m_isStoring(false)
|
||||
{
|
||||
loadSettings();
|
||||
|
||||
// Setup AutoFill userscript
|
||||
QWebEngineScript script;
|
||||
script.setName(QSL("_qupzilla_autofill"));
|
||||
script.setInjectionPoint(QWebEngineScript::DocumentReady);
|
||||
script.setWorldId(QWebEngineScript::MainWorld);
|
||||
script.setRunsOnSubFrames(true);
|
||||
script.setSourceCode(Scripts::setupFormObserver());
|
||||
mApp->webProfile()->scripts()->insert(script);
|
||||
}
|
||||
|
||||
PasswordManager* AutoFill::passwordManager() const
|
||||
@ -166,75 +177,18 @@ void AutoFill::removeAllEntries()
|
||||
m_manager->removeAllEntries();
|
||||
}
|
||||
|
||||
// If password was filled in the page, returns all saved passwords on this page
|
||||
QVector<PasswordEntry> AutoFill::completeFrame(QWebEngineFrame* frame)
|
||||
void AutoFill::saveForm(QWebEnginePage *page, const QUrl &frameUrl, const PageFormData &formData)
|
||||
{
|
||||
bool completed = false;
|
||||
QVector<PasswordEntry> list;
|
||||
|
||||
if (!frame) {
|
||||
return list;
|
||||
}
|
||||
|
||||
#if QTWEBENGINE_DISABLED
|
||||
const QUrl frameUrl = QzTools::frameUrl(frame);
|
||||
if (!isStored(frameUrl)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
list = getFormData(frameUrl);
|
||||
#endif
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
const PasswordEntry entry = list.first();
|
||||
|
||||
PageFormCompleter completer;
|
||||
completed = completer.completeFormData(frame, entry.data);
|
||||
}
|
||||
|
||||
if (!completed) {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void AutoFill::post(const QNetworkRequest &request, const QByteArray &outgoingData)
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
Q_UNUSED(outgoingData)
|
||||
#if QTWEBENGINE_DISABLED
|
||||
// Don't save in private browsing
|
||||
if (mApp->isPrivate()) {
|
||||
if (mApp->isPrivate() || !page)
|
||||
return;
|
||||
}
|
||||
|
||||
QWebEngineFrame* frame = qobject_cast<QWebEngineFrame*>(request.originatingObject());
|
||||
if (!frame) {
|
||||
WebView* webView = qobject_cast<WebView*>(page->view());
|
||||
if (!webView)
|
||||
return;
|
||||
}
|
||||
|
||||
WebPage* webPage = qobject_cast<WebPage*>(frame->page());
|
||||
if (!webPage) {
|
||||
if (!isStoringEnabled(frameUrl))
|
||||
return;
|
||||
}
|
||||
|
||||
WebView* webView = qobject_cast<WebView*>(webPage->view());
|
||||
if (!webView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl frameUrl = QzTools::frameUrl(frame);
|
||||
if (!isStoringEnabled(frameUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PageFormCompleter completer;
|
||||
const PageFormData formData = completer.extractFormData(frame, outgoingData);
|
||||
|
||||
if (!formData.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PasswordEntry updateData;
|
||||
|
||||
@ -261,7 +215,24 @@ void AutoFill::post(const QNetworkRequest &request, const QByteArray &outgoingDa
|
||||
|
||||
AutoFillNotification* aWidget = new AutoFillNotification(frameUrl, formData, updateData);
|
||||
webView->addNotification(aWidget);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns all saved passwords on this page
|
||||
QVector<PasswordEntry> AutoFill::completePage(QWebEnginePage *page, const QUrl &frameUrl)
|
||||
{
|
||||
QVector<PasswordEntry> list;
|
||||
|
||||
if (!page || !isStored(frameUrl))
|
||||
return list;
|
||||
|
||||
list = getFormData(frameUrl);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
const PasswordEntry entry = list.first();
|
||||
page->runJavaScript(Scripts::completeFormData(entry.data));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QByteArray AutoFill::exportPasswords()
|
||||
|
@ -23,8 +23,7 @@
|
||||
#include "qzcommon.h"
|
||||
|
||||
class QUrl;
|
||||
class QWebEngineFrame;
|
||||
class QWebElement;
|
||||
class QWebEnginePage;
|
||||
class QNetworkRequest;
|
||||
|
||||
class BrowserWindow;
|
||||
@ -32,6 +31,16 @@ class PasswordManager;
|
||||
struct PageFormData;
|
||||
struct PasswordEntry;
|
||||
|
||||
struct PageFormData {
|
||||
QString username;
|
||||
QString password;
|
||||
QByteArray postData;
|
||||
|
||||
bool isValid() const {
|
||||
return !password.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
class QUPZILLA_EXPORT AutoFill : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -60,8 +69,8 @@ public:
|
||||
void removeEntry(const PasswordEntry &entry);
|
||||
void removeAllEntries();
|
||||
|
||||
void post(const QNetworkRequest &request, const QByteArray &outgoingData);
|
||||
QVector<PasswordEntry> completeFrame(QWebEngineFrame *frame);
|
||||
void saveForm(QWebEnginePage *page, const QUrl &frameUrl, const PageFormData &formData);
|
||||
QVector<PasswordEntry> completePage(QWebEnginePage *page, const QUrl &frameUrl);
|
||||
|
||||
QByteArray exportPasswords();
|
||||
bool importPasswords(const QByteArray &data);
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include "qzcommon.h"
|
||||
#include "animatedwidget.h"
|
||||
#include "pageformcompleter.h"
|
||||
#include "passwordmanager.h"
|
||||
#include "autofill.h"
|
||||
|
||||
|
@ -17,11 +17,11 @@
|
||||
* ============================================================ */
|
||||
#include "autofillwidget.h"
|
||||
#include "ui_autofillwidget.h"
|
||||
#include "pageformcompleter.h"
|
||||
#include "autofill.h"
|
||||
#include "qztools.h"
|
||||
#include "webview.h"
|
||||
#include "webpage.h"
|
||||
#include "scripts.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
@ -67,9 +67,7 @@ void AutoFillWidget::loginToPage()
|
||||
|
||||
if (ok && QzTools::containsIndex(m_data, index)) {
|
||||
const PasswordEntry entry = m_data.at(index);
|
||||
|
||||
PageFormCompleter completer;
|
||||
completer.completeFormData(m_view->page(), entry.data);
|
||||
m_view->page()->runJavaScript(Scripts::completeFormData(entry.data));
|
||||
}
|
||||
|
||||
close();
|
||||
|
@ -1,284 +0,0 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2013-2014 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "pageformcompleter.h"
|
||||
#include "qzregexp.h"
|
||||
|
||||
#include <QWebEnginePage>
|
||||
#include <QUrlQuery>
|
||||
|
||||
PageFormCompleter::PageFormCompleter()
|
||||
: m_page(0)
|
||||
, m_frame(0)
|
||||
{
|
||||
}
|
||||
|
||||
PageFormData PageFormCompleter::extractFormData(QWebEnginePage* page, const QByteArray &postData)
|
||||
{
|
||||
m_page = page;
|
||||
return extractFormData(postData);
|
||||
}
|
||||
|
||||
PageFormData PageFormCompleter::extractFormData(QWebEngineFrame* frame, const QByteArray &postData)
|
||||
{
|
||||
m_frame = frame;
|
||||
return extractFormData(postData);
|
||||
}
|
||||
|
||||
bool PageFormCompleter::completeFormData(QWebEnginePage* page, const QByteArray &data)
|
||||
{
|
||||
m_page = page;
|
||||
return completeFormData(data);
|
||||
}
|
||||
|
||||
bool PageFormCompleter::completeFormData(QWebEngineFrame* frame, const QByteArray &data)
|
||||
{
|
||||
m_frame = frame;
|
||||
return completeFormData(data);
|
||||
}
|
||||
|
||||
PageFormData PageFormCompleter::extractFormData(const QByteArray &postData) const
|
||||
{
|
||||
QString usernameValue;
|
||||
QString passwordValue;
|
||||
|
||||
QByteArray data = convertWebKitFormBoundaryIfNecessary(postData);
|
||||
PageFormData formData = {QString(), QString(), data};
|
||||
|
||||
if (data.isEmpty() || !data.contains('=')) {
|
||||
return formData;
|
||||
}
|
||||
|
||||
const QueryItems queryItems = createQueryItems(data);
|
||||
|
||||
if (queryItems.isEmpty()) {
|
||||
return formData;
|
||||
}
|
||||
|
||||
#if QTWEBENGINE_DISABLED
|
||||
const QWebElementCollection allForms = getAllElementsFromPage("form");
|
||||
|
||||
// Find form that contains password value sent in data
|
||||
foreach (const QWebElement &formElement, allForms) {
|
||||
bool found = false;
|
||||
const QWebElementCollection inputs = formElement.findAll("input[type=\"password\"]");
|
||||
|
||||
foreach (QWebElement inputElement, inputs) {
|
||||
const QString passName = inputElement.attribute("name");
|
||||
const QString passValue = inputElement.evaluateJavaScript("this.value").toString();
|
||||
|
||||
if (queryItemsContains(queryItems, passName, passValue)) {
|
||||
// Set passwordValue if not empty (to make it possible extract forms without username field)
|
||||
passwordValue = passValue;
|
||||
|
||||
const QueryItem item = findUsername(formElement);
|
||||
if (queryItemsContains(queryItems, item.first, item.second)) {
|
||||
usernameValue = item.second;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// It is necessary only to find password, as there may be form without username field
|
||||
if (passwordValue.isEmpty()) {
|
||||
return formData;
|
||||
}
|
||||
|
||||
formData.username = usernameValue;
|
||||
formData.password = passwordValue;
|
||||
#endif
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
// Returns if any data was actually filled in page
|
||||
bool PageFormCompleter::completeFormData(const QByteArray &data) const
|
||||
{
|
||||
bool completed = false;
|
||||
const QueryItems queryItems = createQueryItems(data);
|
||||
|
||||
// Input types that are being completed
|
||||
QStringList inputTypes;
|
||||
inputTypes << "text" << "password" << "email";
|
||||
|
||||
#if QTWEBENGINE_DISABLED
|
||||
// Find all input elements in the page
|
||||
const QWebElementCollection inputs = getAllElementsFromPage("input");
|
||||
|
||||
for (int i = 0; i < queryItems.count(); i++) {
|
||||
const QString key = queryItems.at(i).first;
|
||||
const QString value = queryItems.at(i).second;
|
||||
|
||||
for (int i = 0; i < inputs.count(); i++) {
|
||||
QWebElement element = inputs.at(i);
|
||||
const QString typeAttr = element.attribute("type");
|
||||
|
||||
if (!inputTypes.contains(typeAttr) && !typeAttr.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == element.attribute("name")) {
|
||||
completed = true;
|
||||
element.setAttribute("value", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return completed;
|
||||
}
|
||||
|
||||
bool PageFormCompleter::queryItemsContains(const QueryItems &queryItems, const QString &attributeName,
|
||||
const QString &attributeValue) const
|
||||
{
|
||||
if (attributeName.isEmpty() || attributeValue.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < queryItems.count(); i++) {
|
||||
const QueryItem item = queryItems.at(i);
|
||||
|
||||
if (item.first == attributeName) {
|
||||
return item.second == attributeValue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray PageFormCompleter::convertWebKitFormBoundaryIfNecessary(const QByteArray &data) const
|
||||
{
|
||||
/* Sometimes, data are passed in this format:
|
||||
*
|
||||
* ------WebKitFormBoundary0bBp3bFMdGwqanMp
|
||||
* Content-Disposition: form-data; name="name-of-attribute"
|
||||
*
|
||||
* value-of-attribute
|
||||
* ------WebKitFormBoundary0bBp3bFMdGwqanMp--
|
||||
*
|
||||
* So this function converts this format into name=value& format
|
||||
*/
|
||||
|
||||
if (!data.contains(QByteArray("------WebKitFormBoundary"))) {
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray formatedData;
|
||||
QzRegExp rx("name=\"(.*)------WebKitFormBoundary");
|
||||
rx.setMinimal(true);
|
||||
|
||||
int pos = 0;
|
||||
while ((pos = rx.indexIn(data, pos)) != -1) {
|
||||
QString string = rx.cap(1);
|
||||
pos += rx.matchedLength();
|
||||
|
||||
int endOfAttributeName = string.indexOf(QLatin1Char('"'));
|
||||
if (endOfAttributeName == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString attrName = string.left(endOfAttributeName);
|
||||
QString attrValue = string.mid(endOfAttributeName + 1).trimmed().remove(QLatin1Char('\n'));
|
||||
|
||||
if (attrName.isEmpty() || attrValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
formatedData.append(attrName + "=" + attrValue + "&");
|
||||
}
|
||||
|
||||
return formatedData;
|
||||
}
|
||||
|
||||
PageFormCompleter::QueryItem PageFormCompleter::findUsername(const QWebElement &form) const
|
||||
{
|
||||
Q_UNUSED(form)
|
||||
#if QTWEBENGINE_DISABLED
|
||||
// Try to find username (or email) field in the form.
|
||||
QStringList selectors;
|
||||
selectors << "input[type=\"text\"][name*=\"user\"]"
|
||||
<< "input[type=\"text\"][name*=\"name\"]"
|
||||
<< "input[type=\"text\"]"
|
||||
<< "input[type=\"email\"]"
|
||||
<< "input:not([type=\"hidden\"][type=\"password\"])";
|
||||
|
||||
foreach (const QString &selector, selectors) {
|
||||
const QWebElementCollection inputs = form.findAll(selector);
|
||||
foreach (QWebElement element, inputs) {
|
||||
const QString name = element.attribute("name");
|
||||
const QString value = element.evaluateJavaScript("this.value").toString();
|
||||
|
||||
if (!name.isEmpty() && !value.isEmpty()) {
|
||||
QueryItem item;
|
||||
item.first = name;
|
||||
item.second = value;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return QueryItem();
|
||||
}
|
||||
|
||||
PageFormCompleter::QueryItems PageFormCompleter::createQueryItems(QByteArray data) const
|
||||
{
|
||||
// QUrlQuery/QUrl never encodes/decodes + and spaces
|
||||
data.replace('+', ' ');
|
||||
|
||||
QUrlQuery query;
|
||||
query.setQuery(data);
|
||||
QueryItems arguments = query.queryItems(QUrl::FullyDecoded);
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
#if QTWEBENGINE_DISABLED
|
||||
QWebElementCollection PageFormCompleter::getAllElementsFromPage(const QString &selector) const
|
||||
{
|
||||
QWebElementCollection list;
|
||||
|
||||
if (!m_page && !m_frame)
|
||||
return list;
|
||||
|
||||
if (m_frame)
|
||||
return m_frame->findAllElements(selector);
|
||||
|
||||
if (!m_page->mainFrame())
|
||||
return list;
|
||||
|
||||
QList<QWebFrame*> frames;
|
||||
frames.append(m_page->mainFrame());
|
||||
|
||||
while (!frames.isEmpty()) {
|
||||
QWebFrame* frame = frames.takeFirst();
|
||||
if (frame && !frame->documentElement().isNull()) {
|
||||
list.append(frame->findAllElements(selector));
|
||||
frames += frame->childFrames();
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,73 +0,0 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2013-2014 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#ifndef PAGEFORMCOMPLETER_H
|
||||
#define PAGEFORMCOMPLETER_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "qzcommon.h"
|
||||
|
||||
class QWebEnginePage;
|
||||
class QWebEngineFrame;
|
||||
class QWebElement;
|
||||
class QWebElementCollection;
|
||||
|
||||
struct PageFormData {
|
||||
QString username;
|
||||
QString password;
|
||||
QByteArray postData;
|
||||
|
||||
bool isValid() const {
|
||||
return !password.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
class QUPZILLA_EXPORT PageFormCompleter
|
||||
{
|
||||
public:
|
||||
explicit PageFormCompleter();
|
||||
|
||||
PageFormData extractFormData(QWebEnginePage* page, const QByteArray &postData);
|
||||
PageFormData extractFormData(QWebEngineFrame* frame, const QByteArray &postData);
|
||||
|
||||
bool completeFormData(QWebEnginePage* page, const QByteArray &data);
|
||||
bool completeFormData(QWebEngineFrame* frame, const QByteArray &data);
|
||||
|
||||
private:
|
||||
typedef QPair<QString, QString> QueryItem;
|
||||
typedef QList<QPair<QString, QString> > QueryItems;
|
||||
|
||||
PageFormData extractFormData(const QByteArray &postData) const;
|
||||
bool completeFormData(const QByteArray &data) const;
|
||||
|
||||
bool queryItemsContains(const QueryItems &queryItems, const QString &attributeName,
|
||||
const QString &attributeValue) const;
|
||||
QByteArray convertWebKitFormBoundaryIfNecessary(const QByteArray &data) const;
|
||||
QueryItem findUsername(const QWebElement &form) const;
|
||||
QueryItems createQueryItems(QByteArray data) const;
|
||||
#if QTWEBENGINE_DISABLED
|
||||
QWebElementCollection getAllElementsFromPage(const QString &selector) const;
|
||||
#endif
|
||||
|
||||
QWebEnginePage* m_page;
|
||||
QWebEngineFrame* m_frame;
|
||||
};
|
||||
|
||||
#endif // PAGEFORMCOMPLETER_H
|
@ -76,7 +76,6 @@ SOURCES += \
|
||||
autofill/autofillicon.cpp \
|
||||
autofill/autofillnotification.cpp \
|
||||
autofill/autofillwidget.cpp \
|
||||
autofill/pageformcompleter.cpp \
|
||||
autofill/passwordbackends/databaseencryptedpasswordbackend.cpp \
|
||||
autofill/passwordbackends/databasepasswordbackend.cpp \
|
||||
autofill/passwordbackends/passwordbackend.cpp \
|
||||
@ -237,7 +236,8 @@ SOURCES += \
|
||||
webtab/tabbedwebview.cpp \
|
||||
webtab/webtab.cpp \
|
||||
adblock/adblockmatcher.cpp \
|
||||
tools/scripts.cpp
|
||||
tools/scripts.cpp \
|
||||
webengine/javascript/autofilljsobject.cpp
|
||||
|
||||
HEADERS += \
|
||||
3rdparty/ecwin7.h \
|
||||
@ -272,7 +272,6 @@ HEADERS += \
|
||||
autofill/autofillicon.h \
|
||||
autofill/autofillnotification.h \
|
||||
autofill/autofillwidget.h \
|
||||
autofill/pageformcompleter.h \
|
||||
autofill/passwordbackends/databaseencryptedpasswordbackend.h \
|
||||
autofill/passwordbackends/databasepasswordbackend.h \
|
||||
autofill/passwordbackends/passwordbackend.h \
|
||||
@ -326,7 +325,7 @@ HEADERS += \
|
||||
navigation/siteicon.h \
|
||||
navigation/websearchbar.h \
|
||||
#network/cabundleupdater.h \
|
||||
network/networkmanager.h \
|
||||
#network/networkmanager.h \
|
||||
network/networkmanagerproxy.h \
|
||||
network/networkproxyfactory.h \
|
||||
network/pac/pacdatetime.h \
|
||||
@ -437,7 +436,8 @@ HEADERS += \
|
||||
webtab/tabbedwebview.h \
|
||||
webtab/webtab.h \
|
||||
adblock/adblockmatcher.h \
|
||||
tools/scripts.h
|
||||
tools/scripts.h \
|
||||
webengine/javascript/autofilljsobject.h
|
||||
|
||||
FORMS += \
|
||||
adblock/adblockaddsubscriptiondialog.ui \
|
||||
|
@ -15,7 +15,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "networkmanager.h"
|
||||
#include "browserwindow.h"
|
||||
#include "autofill.h"
|
||||
#include "networkmanagerproxy.h"
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "editsearchengine.h"
|
||||
#include "iconprovider.h"
|
||||
#include "mainapplication.h"
|
||||
#include "networkmanager.h"
|
||||
#include "opensearchreader.h"
|
||||
#include "opensearchengine.h"
|
||||
#include "settings.h"
|
||||
|
@ -49,6 +49,57 @@ QString Scripts::setupWebChannel()
|
||||
return source.arg(QzTools::readAllFileContents(QSL(":/html/qwebchannel.js")));
|
||||
}
|
||||
|
||||
QString Scripts::setupFormObserver()
|
||||
{
|
||||
QString source = QL1S("(function() {"
|
||||
"function findUsername(inputs) {"
|
||||
" for (var i = 0; i < inputs.length; ++i)"
|
||||
" if (inputs[i].type == 'text' && inputs[i].value.length && inputs[i].name.indexOf('user') != -1)"
|
||||
" return inputs[i].value;"
|
||||
" for (var i = 0; i < inputs.length; ++i)"
|
||||
" if (inputs[i].type == 'text' && inputs[i].value.length && inputs[i].name.indexOf('name') != -1)"
|
||||
" return inputs[i].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);"
|
||||
"}"
|
||||
"for (var i = 0; i < document.forms.length; ++i)"
|
||||
" registerForm(document.forms[i]);"
|
||||
"})()");
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
QString Scripts::setCss(const QString &css)
|
||||
{
|
||||
QString source = QL1S("(function() {"
|
||||
@ -94,3 +145,36 @@ QString Scripts::sendPostData(const QUrl &url, const QByteArray &data)
|
||||
|
||||
return source.arg(url.toString(), values);
|
||||
}
|
||||
|
||||
QString Scripts::completeFormData(const QByteArray &data)
|
||||
{
|
||||
QString source = QL1S("(function() {"
|
||||
"var data = '%1'.split('&');"
|
||||
"var inputs = [];"
|
||||
"var frames = [window, window.frames];"
|
||||
"for (var i = 0; i < frames.length; ++i) {"
|
||||
" var finputs = frames[i].document.getElementsByTagName('input');"
|
||||
" for (var j = 0; j < finputs.length; ++j) {"
|
||||
" var type = finputs[j].type.toLowerCase();"
|
||||
" if (type == 'text' || type == 'password' || type == 'email')"
|
||||
" inputs.push(finputs[j]);"
|
||||
" }"
|
||||
"}"
|
||||
"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];"
|
||||
" if (input.name == key)"
|
||||
" input.value = val;"
|
||||
" }"
|
||||
"}"
|
||||
"})()");
|
||||
|
||||
QString d = data;
|
||||
d.replace(QL1S("'"), QL1S("\\'"));
|
||||
return source.arg(d);
|
||||
}
|
||||
|
@ -29,8 +29,11 @@ class QUPZILLA_EXPORT Scripts
|
||||
{
|
||||
public:
|
||||
static QString setupWebChannel();
|
||||
static QString setupFormObserver();
|
||||
|
||||
static QString setCss(const QString &css);
|
||||
static QString sendPostData(const QUrl &url, const QByteArray &data);
|
||||
static QString completeFormData(const QByteArray &data);
|
||||
};
|
||||
|
||||
#endif // SCRIPTS_H
|
||||
|
40
src/lib/webengine/javascript/autofilljsobject.cpp
Normal file
40
src/lib/webengine/javascript/autofilljsobject.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/* ============================================================
|
||||
* QupZilla - QtWebEngine based browser
|
||||
* Copyright (C) 2015 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
|
||||
#include "autofilljsobject.h"
|
||||
#include "externaljsobject.h"
|
||||
#include "mainapplication.h"
|
||||
#include "autofill.h"
|
||||
#include "webpage.h"
|
||||
|
||||
AutoFillJsObject::AutoFillJsObject(ExternalJsObject *parent)
|
||||
: QObject(parent)
|
||||
, m_jsObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void AutoFillJsObject::formSubmitted(const QString &frameUrl, const QString &username, const QString &password, const QByteArray &data)
|
||||
{
|
||||
PageFormData formData;
|
||||
formData.username = username;
|
||||
formData.password = password;
|
||||
formData.postData = data;
|
||||
|
||||
mApp->autoFill()->saveForm(m_jsObject->page(), QUrl(frameUrl), formData);
|
||||
}
|
||||
|
39
src/lib/webengine/javascript/autofilljsobject.h
Normal file
39
src/lib/webengine/javascript/autofilljsobject.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* ============================================================
|
||||
* QupZilla - QtWebEngine based browser
|
||||
* Copyright (C) 2015 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef AUTOFILLJSOBJECT_H
|
||||
#define AUTOFILLJSOBJECT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class ExternalJsObject;
|
||||
|
||||
class AutoFillJsObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AutoFillJsObject(ExternalJsObject *parent);
|
||||
|
||||
public slots:
|
||||
void formSubmitted(const QString &frameUrl, const QString &username, const QString &password, const QByteArray &data);
|
||||
|
||||
private:
|
||||
ExternalJsObject *m_jsObject;
|
||||
};
|
||||
|
||||
#endif // AUTOFILLJSOBJECT_H
|
@ -21,13 +21,20 @@
|
||||
#include "speeddial.h"
|
||||
#include "webpage.h"
|
||||
#include "searchenginesmanager.h"
|
||||
#include "autofilljsobject.h"
|
||||
|
||||
ExternalJsObject::ExternalJsObject(WebPage *page)
|
||||
: QObject(page)
|
||||
, m_page(page)
|
||||
, m_autoFill(new AutoFillJsObject(this))
|
||||
{
|
||||
}
|
||||
|
||||
WebPage *ExternalJsObject::page() const
|
||||
{
|
||||
return m_page;
|
||||
}
|
||||
|
||||
void ExternalJsObject::AddSearchProvider(const QString &engineUrl)
|
||||
{
|
||||
mApp->searchEnginesManager()->addEngine(QUrl(engineUrl));
|
||||
@ -46,3 +53,8 @@ QObject *ExternalJsObject::speedDial() const
|
||||
|
||||
return mApp->plugins()->speedDial();
|
||||
}
|
||||
|
||||
QObject *ExternalJsObject::autoFill() const
|
||||
{
|
||||
return m_autoFill;
|
||||
}
|
||||
|
@ -21,24 +21,29 @@
|
||||
#include <QObject>
|
||||
|
||||
class WebPage;
|
||||
class AutoFillJsObject;
|
||||
|
||||
class ExternalJsObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QObject* speedDial READ speedDial CONSTANT)
|
||||
Q_PROPERTY(QObject* autoFill READ autoFill CONSTANT)
|
||||
|
||||
public:
|
||||
explicit ExternalJsObject(WebPage *page);
|
||||
|
||||
WebPage *page() const;
|
||||
|
||||
public slots:
|
||||
void AddSearchProvider(const QString &engineUrl);
|
||||
int IsSearchProviderInstalled(const QString &engineURL);
|
||||
|
||||
private:
|
||||
QObject *speedDial() const;
|
||||
QObject *autoFill() const;
|
||||
|
||||
WebPage *m_page;
|
||||
|
||||
AutoFillJsObject *m_autoFill;
|
||||
};
|
||||
|
||||
#endif // EXTERNALJSOBJECT_H
|
||||
|
@ -211,6 +211,9 @@ void WebPage::finished()
|
||||
|
||||
// AdBlock
|
||||
cleanBlockedObjects();
|
||||
|
||||
// AutoFill
|
||||
m_passwordEntries = mApp->autoFill()->completePage(this, url());
|
||||
}
|
||||
|
||||
void WebPage::watchedFileChanged(const QString &file)
|
||||
@ -293,9 +296,17 @@ void WebPage::desktopServicesOpen(const QUrl &url)
|
||||
|
||||
void WebPage::setupWebChannel()
|
||||
{
|
||||
QWebChannel *old = webChannel();
|
||||
const QString objectName = QSL("qz_object");
|
||||
|
||||
QWebChannel *channel = new QWebChannel(this);
|
||||
channel->registerObject(QSL("qz_object"), new ExternalJsObject(this));
|
||||
setWebChannel(channel);
|
||||
|
||||
if (old) {
|
||||
delete old->registeredObjects().value(objectName);
|
||||
delete old;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPage::windowCloseRequested()
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "browserwindow.h"
|
||||
#include "webpage.h"
|
||||
#include "tabwidget.h"
|
||||
#include "networkmanager.h"
|
||||
#include "mainapplication.h"
|
||||
#include "tabbar.h"
|
||||
#include "webtab.h"
|
||||
|
11
tests/form-iframe.html
Normal file
11
tests/form-iframe.html
Normal file
@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>Form completion test in iframe</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Form completion test in iframe</h2>
|
||||
|
||||
<iframe width="100%" height="80%" src="form.html"></iframe>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user