244 lines
7.6 KiB
C++
244 lines
7.6 KiB
C++
#include "JavascriptInvocation.h"
|
|
#include "WebPage.h"
|
|
#include "InvocationResult.h"
|
|
#include <QApplication>
|
|
#include <QEvent>
|
|
#include <QContextMenuEvent>
|
|
|
|
QMap<QString, Qt::KeyboardModifiers> JavascriptInvocation::m_modifiersMap(JavascriptInvocation::makeModifiersMap());
|
|
|
|
JavascriptInvocation::JavascriptInvocation(const QString &functionName, bool allowUnattached, const QStringList &arguments, WebPage *page, QObject *parent) : QObject(parent) {
|
|
m_functionName = functionName;
|
|
m_allowUnattached = allowUnattached;
|
|
m_arguments = arguments;
|
|
m_page = page;
|
|
}
|
|
|
|
QString &JavascriptInvocation::functionName() {
|
|
return m_functionName;
|
|
}
|
|
|
|
bool JavascriptInvocation::allowUnattached() {
|
|
return m_allowUnattached;
|
|
}
|
|
|
|
QStringList &JavascriptInvocation::arguments() {
|
|
return m_arguments;
|
|
}
|
|
|
|
QVariant JavascriptInvocation::getError() {
|
|
return m_error;
|
|
}
|
|
|
|
void JavascriptInvocation::setError(QVariant error) {
|
|
m_error = error;
|
|
}
|
|
|
|
InvocationResult JavascriptInvocation::invoke(QWebFrame *frame) {
|
|
|
|
frame->addToJavaScriptWindowObject("CapybaraInvocation", this);
|
|
QVariant result = frame->evaluateJavaScript("Capybara.invoke()");
|
|
if (getError().isValid())
|
|
return InvocationResult(getError(), true);
|
|
else {
|
|
if (functionName() == "leftClick") {
|
|
// Don't trigger the left click from JS incase the frame closes
|
|
QVariantMap qm = result.toMap();
|
|
leftClick(qm["absoluteX"].toInt(), qm["absoluteY"].toInt(), qm["keys"].value<QVariantList>());
|
|
}
|
|
return InvocationResult(result);
|
|
}
|
|
}
|
|
|
|
Qt::KeyboardModifiers JavascriptInvocation::modifiers(const QVariantList& keys){
|
|
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
|
|
for (int i = 0; i < keys.length(); i++) {
|
|
modifiers |= m_modifiersMap.value(keys[i].toString(), Qt::NoModifier);
|
|
}
|
|
return modifiers;
|
|
}
|
|
|
|
void JavascriptInvocation::leftClick(int x, int y, QVariantList keys) {
|
|
QPoint mousePos(x, y);
|
|
|
|
m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton, modifiers(keys));
|
|
m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton, modifiers(keys));
|
|
}
|
|
|
|
void JavascriptInvocation::rightClick(int x, int y, QVariantList keys) {
|
|
QPoint mousePos(x, y);
|
|
m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::RightButton, modifiers(keys));
|
|
|
|
// swallowContextMenuEvent tries to fire contextmenu event in html page
|
|
QContextMenuEvent *event = new QContextMenuEvent(QContextMenuEvent::Mouse, mousePos, QCursor::pos(), modifiers(keys));
|
|
m_page->swallowContextMenuEvent(event);
|
|
|
|
m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::RightButton, modifiers(keys));
|
|
}
|
|
|
|
void JavascriptInvocation::doubleClick(int x, int y, QVariantList keys) {
|
|
QPoint mousePos(x, y);
|
|
|
|
m_page->mouseEvent(QEvent::MouseButtonDblClick, mousePos, Qt::LeftButton, modifiers(keys));
|
|
m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton, modifiers(keys));
|
|
}
|
|
|
|
bool JavascriptInvocation::clickTest(QWebElement element, int absoluteX, int absoluteY) {
|
|
return m_page->clickTest(element, absoluteX, absoluteY);
|
|
}
|
|
|
|
QVariantMap JavascriptInvocation::clickPosition(QWebElement element, int left, int top, int width, int height) {
|
|
QRect elementBox(left, top, width, height);
|
|
QRect viewport(QPoint(0, 0), m_page->viewportSize());
|
|
QRect boundedBox = elementBox.intersected(viewport);
|
|
QPoint mousePos = boundedBox.center();
|
|
|
|
QVariantMap m;
|
|
m["relativeX"] = mousePos.x();
|
|
m["relativeY"] = mousePos.y();
|
|
m["relativeTop"] = boundedBox.top();
|
|
m["relativeLeft"] = boundedBox.left();
|
|
|
|
QWebFrame *parent = element.webFrame();
|
|
while (parent) {
|
|
elementBox.translate(parent->geometry().topLeft());
|
|
parent = parent->parentFrame();
|
|
}
|
|
|
|
boundedBox = elementBox.intersected(viewport);
|
|
mousePos = boundedBox.center();
|
|
m["absoluteTop"] = boundedBox.top();
|
|
m["absoluteLeft"] = boundedBox.left();
|
|
m["absoluteX"] = mousePos.x();
|
|
m["absoluteY"] = mousePos.y();
|
|
|
|
return m;
|
|
}
|
|
|
|
void JavascriptInvocation::hover(int absoluteX, int absoluteY) {
|
|
QPoint mousePos(absoluteX, absoluteY);
|
|
hover(mousePos);
|
|
}
|
|
|
|
void JavascriptInvocation::hover(const QPoint &mousePos) {
|
|
m_page->mouseEvent(QEvent::MouseMove, mousePos, Qt::NoButton);
|
|
}
|
|
|
|
int JavascriptInvocation::keyCodeFor(const QChar &key) {
|
|
switch(key.unicode()) {
|
|
case 0x18:
|
|
return Qt::Key_Cancel;
|
|
case 0x08:
|
|
return Qt::Key_Backspace;
|
|
case 0x09:
|
|
return Qt::Key_Tab;
|
|
case 0x0A:
|
|
return Qt::Key_Return;
|
|
case 0x1B:
|
|
return Qt::Key_Escape;
|
|
case 0x7F:
|
|
return Qt::Key_Delete;
|
|
default:
|
|
int keyCode = key.toUpper().toLatin1();
|
|
if (keyCode >= Qt::Key_Space || keyCode <= Qt::Key_AsciiTilde)
|
|
return keyCode;
|
|
else
|
|
return Qt::Key_unknown;
|
|
}
|
|
}
|
|
|
|
int JavascriptInvocation::keyCodeForName(const QString &keyName) {
|
|
const QMetaObject &mo = JavascriptInvocation::staticMetaObject;
|
|
int prop_index = mo.indexOfProperty("key_enum");
|
|
QMetaProperty metaProperty = mo.property(prop_index);
|
|
QMetaEnum metaEnum = metaProperty.enumerator();
|
|
|
|
QByteArray array ((QString("Key_") + keyName).toStdString().c_str());
|
|
return metaEnum.keyToValue(array);
|
|
// return Qt::Key_unknown;
|
|
}
|
|
|
|
void JavascriptInvocation::keypress(QChar key) {
|
|
int keyCode = keyCodeFor(key);
|
|
QKeyEvent event(QKeyEvent::KeyPress, keyCode, m_currentModifiers, (m_currentModifiers ? QString() : key));
|
|
QApplication::sendEvent(m_page, &event);
|
|
event = QKeyEvent(QKeyEvent::KeyRelease, keyCode, m_currentModifiers);
|
|
QApplication::sendEvent(m_page, &event);
|
|
}
|
|
|
|
void JavascriptInvocation::namedKeypress(QString keyName, QString modifiers){
|
|
Qt::KeyboardModifiers key_modifiers(m_currentModifiers);
|
|
if (modifiers == "Keypad") {
|
|
key_modifiers |= Qt::KeypadModifier;
|
|
};
|
|
int keyCode = keyCodeForName(keyName);
|
|
QKeyEvent event(QKeyEvent::KeyPress, keyCode, key_modifiers);
|
|
QApplication::sendEvent(m_page, &event);
|
|
event = QKeyEvent(QKeyEvent::KeyRelease, keyCode, key_modifiers);
|
|
QApplication::sendEvent(m_page, &event);
|
|
}
|
|
|
|
void JavascriptInvocation::namedKeydown(QString keyName){
|
|
int keyCode = keyCodeForName(keyName);
|
|
QKeyEvent event(QKeyEvent::KeyPress, keyCode, m_currentModifiers);
|
|
QApplication::sendEvent(m_page, &event);
|
|
|
|
switch(keyCode){
|
|
case Qt::Key_Shift:
|
|
m_currentModifiers |= Qt::ShiftModifier;
|
|
break;
|
|
case Qt::Key_Control:
|
|
m_currentModifiers |= Qt::ControlModifier;
|
|
break;
|
|
case Qt::Key_Alt:
|
|
m_currentModifiers |= Qt::AltModifier;
|
|
break;
|
|
case Qt::Key_Meta:
|
|
m_currentModifiers |= Qt::MetaModifier;
|
|
break;
|
|
};
|
|
}
|
|
|
|
void JavascriptInvocation::namedKeyup(QString keyName){
|
|
int keyCode = keyCodeForName(keyName);
|
|
QKeyEvent event(QKeyEvent::KeyRelease, keyCode, m_currentModifiers, 0);
|
|
QApplication::sendEvent(m_page, &event);
|
|
|
|
switch(keyCode){
|
|
case Qt::Key_Shift:
|
|
m_currentModifiers &= ~Qt::ShiftModifier;
|
|
break;
|
|
case Qt::Key_Control:
|
|
m_currentModifiers &= ~Qt::ControlModifier;
|
|
break;
|
|
case Qt::Key_Alt:
|
|
m_currentModifiers &= ~Qt::AltModifier;
|
|
break;
|
|
case Qt::Key_Meta:
|
|
m_currentModifiers &= ~Qt::MetaModifier;
|
|
break;
|
|
};
|
|
}
|
|
|
|
const QString JavascriptInvocation::render(void) {
|
|
QString pathTemplate =
|
|
QDir::temp().absoluteFilePath("./click_failed_XXXXXX.png");
|
|
QTemporaryFile file(pathTemplate);
|
|
file.open();
|
|
file.setAutoRemove(false);
|
|
QString path = file.fileName();
|
|
m_page->render(path, QSize(1024, 768));
|
|
return path;
|
|
}
|
|
|
|
QMap<QString, Qt::KeyboardModifiers> JavascriptInvocation::makeModifiersMap(){
|
|
QMap<QString, Qt::KeyboardModifiers> map;
|
|
map["alt"] = Qt::AltModifier;
|
|
map["control"] = Qt::ControlModifier;
|
|
map["meta"] = Qt::MetaModifier;
|
|
map["shift"] = Qt::ShiftModifier;
|
|
return map;
|
|
}
|
|
|
|
|