add event catch method and refactor error messages

Added method that catches specified events from the page
Refactored error messages
This commit is contained in:
zeheyler 2020-10-15 02:32:58 +03:00
parent 6b54ec0733
commit a3ebaf0023
3 changed files with 103 additions and 79 deletions

View File

@ -3,10 +3,11 @@
DevTools::DevTools(QObject* parent) : DevTools::DevTools(QObject* parent) :
QObject(parent), QObject(parent),
idcounter(0), idcounter(0),
idmethod(0),
pagenavigated(false), pagenavigated(false),
translateready(false), translateready(false),
status("Stopped"), status("Stopped"),
session(1) session(0)
{ {
} }
@ -116,30 +117,6 @@ void DevTools::stateChanged(QAbstractSocket::SocketState state)
emit statusChanged(status); emit statusChanged(status);
} }
void DevTools::setNavigated(bool value)
{
mutex.lock();
pagenavigated = value;
mutex.unlock();
}
bool DevTools::getNavigated()
{
return pagenavigated;
}
void DevTools::setTranslate(bool value)
{
mutex.lock();
translateready = value;
mutex.unlock();
}
bool DevTools::getTranslate()
{
return translateready;
}
bool DevTools::SendRequest(QString method, QJsonObject params, QJsonObject& root) bool DevTools::SendRequest(QString method, QJsonObject params, QJsonObject& root)
{ {
if (!isConnected()) if (!isConnected())
@ -183,11 +160,28 @@ bool DevTools::SendRequest(QString method, QJsonObject params, QJsonObject& root
return false; return false;
} }
long DevTools::methodToReceive(QString method, QJsonObject params)
{
QJsonObject json;
long id = idmIncrement();
json.insert("method", method);
json.insert("params", params);
mutex.lock();
mapmethod.insert(std::make_pair(id, json));
mutex.unlock();
return id;
}
long DevTools::idIncrement() long DevTools::idIncrement()
{ {
return ++idcounter; return ++idcounter;
} }
long DevTools::idmIncrement()
{
return ++idmethod;
}
bool DevTools::isConnected() bool DevTools::isConnected()
{ {
if (webSocket.state() == QAbstractSocket::ConnectedState) if (webSocket.state() == QAbstractSocket::ConnectedState)
@ -196,6 +190,29 @@ bool DevTools::isConnected()
return false; return false;
} }
bool DevTools::compareJson(QJsonObject storedparams, QJsonObject params)
{
foreach(const QString & key, storedparams.keys())
{
if (storedparams.value(key).isArray())
return false;
if (storedparams.value(key) != params.value(key))
return false;
if (!compareJson(storedparams.value(key).toObject(), params.value(key).toObject()))
return false;
}
return true;
}
bool DevTools::checkMethod(long id)
{
MapMethod::iterator iter = mapmethod.find(id);
if (iter == mapmethod.end())
return true;
else
return false;
}
void DevTools::onTextMessageReceived(QString message) void DevTools::onTextMessageReceived(QString message)
{ {
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8()); QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
@ -204,23 +221,19 @@ void DevTools::onTextMessageReceived(QString message)
QJsonObject root = doc.object(); QJsonObject root = doc.object();
if (root.contains("method")) if (root.contains("method"))
{ {
if (root.value("method").toString() == "Page.navigatedWithinDocument") for (auto iter = mapmethod.cbegin(); iter != mapmethod.cend();)
{ {
mutex.lock(); if ((iter->second.value("method") == root.value("method"))
pagenavigated = true; && ((iter->second.value("params").toObject().isEmpty())
mutex.unlock(); || (compareJson(iter->second.value("params").toObject(), root.value("params").toObject()))))
}
if (root.value("method").toString() == "DOM.attributeModified")
{
if (root.value("params").toObject().value("value") == "lmt__mobile_share_container")
{ {
mutex.lock(); mutex.lock();
translateready = true; mapmethod.erase(iter++);
mutex.unlock(); mutex.unlock();
} }
++iter;
} }
return; return;
} }
if (root.contains("id")) if (root.contains("id"))
{ {
@ -242,17 +255,18 @@ void DevTools::closeDevTools()
{ {
if (this->mapqueue.size() > 0) if (this->mapqueue.size() > 0)
{ {
MapResponse::iterator iter = this->mapqueue.begin(); MapResponse::iterator iter = mapqueue.begin();
MapResponse::iterator iend = this->mapqueue.end(); MapResponse::iterator iend = mapqueue.end();
for (; iter != iend; iter++) for (; iter != iend; iter++)
{ {
iter->second.set_exception("exception"); iter->second.set_exception("exception");
} }
} }
webSocket.close(); webSocket.close();
mapmethod.clear();
mapqueue.clear(); mapqueue.clear();
idcounter = 0; idcounter = 0;
idmethod = 0;
DWORD exitCode = 0; DWORD exitCode = 0;
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) != FALSE) if (GetExitCodeProcess(processInfo.hProcess, &exitCode) != FALSE)
{ {

View File

@ -7,6 +7,7 @@
using namespace Concurrency; using namespace Concurrency;
typedef std::map<long, task_completion_event<QJsonObject>> MapResponse; typedef std::map<long, task_completion_event<QJsonObject>> MapResponse;
typedef std::map<long, QJsonObject> MapMethod;
class DevTools : public QObject { class DevTools : public QObject {
Q_OBJECT Q_OBJECT
@ -24,12 +25,10 @@ private Q_SLOTS:
public: public:
void startDevTools(QString path, bool headless = false, int port = 9222); void startDevTools(QString path, bool headless = false, int port = 9222);
void closeDevTools(); void closeDevTools();
void setNavigated(bool value); bool checkMethod(long id);
bool getNavigated();
void setTranslate(bool value);
bool getTranslate();
int getSession(); int getSession();
bool SendRequest(QString command, QJsonObject params, QJsonObject& result); bool SendRequest(QString method, QJsonObject params, QJsonObject& root);
long methodToReceive(QString method, QJsonObject params);
QString getStatus(); QString getStatus();
private: private:
@ -37,13 +36,17 @@ private:
bool startChrome(QString path, bool headless = false, int port = 9222); bool startChrome(QString path, bool headless = false, int port = 9222);
bool GetwebSocketDebuggerUrl(QString& url, int port = 9222); bool GetwebSocketDebuggerUrl(QString& url, int port = 9222);
long idIncrement(); long idIncrement();
long idmIncrement();
bool compareJson(QJsonObject storedparams, QJsonObject params);
int session; int session;
QWebSocket webSocket; QWebSocket webSocket;
std::mutex mutex; std::mutex mutex;
MapResponse mapqueue; MapResponse mapqueue;
MapMethod mapmethod;
bool pagenavigated; bool pagenavigated;
bool translateready; bool translateready;
long idcounter; long idcounter;
long idmethod;
PROCESS_INFORMATION processInfo; PROCESS_INFORMATION processInfo;
QString status; QString status;
}; };

View File

@ -10,6 +10,12 @@ bool useCache = true, autostartchrome = false, headlesschrome = true;
int maxSentenceSize = 500, chromeport = 9222; int maxSentenceSize = 500, chromeport = 9222;
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
const wchar_t* ERROR_CHROME = L"Error: chrome not started";
const wchar_t* ERROR_START_CHROME = L"Error: failed to start chrome or to connect to it";
const wchar_t* ERROR_GOT_TIMEOUT = L"Error: timeout (s)";
const wchar_t* ERROR_COMMAND_FAIL = L"Error: command failed";
const wchar_t* ERROR_LANGUAGE = L"Error: target languages do not match";
QString URL = "https://www.deepl.com/en/translator"; QString URL = "https://www.deepl.com/en/translator";
QStringList languages QStringList languages
{ {
@ -26,27 +32,10 @@ QStringList languages
"Spanish: es", "Spanish: es",
}; };
int docfound = -1; int docfound = -1, targetNodeId = -1, session = -1, pageenabled = -1;
int targetNodeId = -1;
int session = -1;
std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devtools) std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devtools)
{ {
if (devtools->getStatus() == "Stopped")
{
return { false, FormatString(L"Error: chrome not started") };
}
if ((devtools->getStatus().startsWith("Fail")) || (devtools->getStatus().startsWith("Unconnected")))
{
return { false, FormatString(L"Error: %s", S(devtools->getStatus())) };
}
if (session != devtools->getSession())
{
session = devtools->getSession();
docfound = -1;
targetNodeId = -1;
}
QString qtext = S(text); QString qtext = S(text);
// Check text for repeated symbols (e.g. only ellipsis) // Check text for repeated symbols (e.g. only ellipsis)
@ -57,10 +46,26 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
break; break;
if ((i + 2) == qtext.length() && (qtext.front() == qtext.back())) if ((i + 2) == qtext.length() && (qtext.front() == qtext.back()))
{ {
return { false, text }; return { true, text };
} }
} }
if (devtools->getStatus() == "Stopped")
{
return { false, FormatString(L"%s", ERROR_CHROME) };
}
if ((devtools->getStatus().startsWith("Fail")) || (devtools->getStatus().startsWith("Unconnected")))
{
return { false, FormatString(L"%s", ERROR_START_CHROME) };
}
if (session != devtools->getSession())
{
session = devtools->getSession();
docfound = -1;
targetNodeId = -1;
pageenabled = -1;
}
// Add spaces near ellipsis for better translation and check for quotes // Add spaces near ellipsis for better translation and check for quotes
qtext.replace(QRegularExpression("[" + QString(8230) + "]" + "[" + QString(8230) + "]" + "[" + QString(8230) + "]"), QString(8230)); qtext.replace(QRegularExpression("[" + QString(8230) + "]" + "[" + QString(8230) + "]" + "[" + QString(8230) + "]"), QString(8230));
qtext.replace(QRegularExpression("[" + QString(8230) + "]" + "[" + QString(8230) + "]"), QString(8230)); qtext.replace(QRegularExpression("[" + QString(8230) + "]" + "[" + QString(8230) + "]"), QString(8230));
@ -73,31 +78,34 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
qtext.chop(1); qtext.chop(1);
} }
QJsonObject root; QJsonObject root;
QJsonObject result;
// Enable page feedback // Enable page feedback
if (!devtools->SendRequest("Page.enable", {}, root)) if (pageenabled == -1)
{ {
return { false, FormatString(L"Error: page enable failed! %s", TRANSLATION_ERROR) }; if (!devtools->SendRequest("Page.enable", {}, root))
{
return { false, FormatString(L"%s", ERROR_COMMAND_FAIL) };
}
pageenabled = 1;
} }
long navigate = devtools->methodToReceive("Page.navigatedWithinDocument", {});
long target = devtools->methodToReceive("DOM.attributeModified", { {"value" , "lmt__mobile_share_container"} });
// Navigate to site // Navigate to site
QString fullurl = URL + "#ja/" + S(translateTo.Copy()) + "/" + qtext; QString fullurl = URL + "#ja/" + S(translateTo.Copy()) + "/" + qtext;
devtools->setNavigated(false);
devtools->setTranslate(false);
if (devtools->SendRequest("Page.navigate", { {"url", fullurl} }, root)) if (devtools->SendRequest("Page.navigate", { {"url", fullurl} }, root))
{ {
// Wait until page is loaded // Wait until page is loaded
float timer = 0; float timer = 0;
int timer_stop = 10; int timer_stop = 10;
while (!devtools->getNavigated() && timer < timer_stop) while (!devtools->checkMethod(navigate) && timer < timer_stop)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
timer += 0.1; timer += 0.1;
} }
if (timer >= timer_stop) if (timer >= timer_stop)
{ {
return { false, FormatString(L"Error: page load timeout %d s! %s", timer_stop, TRANSLATION_ERROR) }; return { false, FormatString(L"%s: %d ", ERROR_GOT_TIMEOUT, timer_stop) };
} }
QString OuterHTML("<div></div>"); QString OuterHTML("<div></div>");
@ -107,7 +115,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
if (!devtools->SendRequest("DOM.getDocument", {}, root)) if (!devtools->SendRequest("DOM.getDocument", {}, root))
{ {
docfound = -1; docfound = -1;
return { false, FormatString(L"Error: getDocument failed! %s", TRANSLATION_ERROR) }; return { false, FormatString(L"%s", ERROR_COMMAND_FAIL) };
} }
docfound = root.value("result").toObject().value("root").toObject().value("nodeId").toInt(); docfound = root.value("result").toObject().value("root").toObject().value("nodeId").toInt();
} }
@ -119,14 +127,14 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
|| (root.value("result").toObject().value("nodeId").toInt() == 0)) || (root.value("result").toObject().value("nodeId").toInt() == 0))
{ {
docfound = -1; docfound = -1;
return { false, FormatString(L"Error: querySelector result failed! %s", TRANSLATION_ERROR) }; return { false, FormatString(L"%s", ERROR_COMMAND_FAIL) };
} }
targetNodeId = root.value("result").toObject().value("nodeId").toInt(); targetNodeId = root.value("result").toObject().value("nodeId").toInt();
} }
// Wait for translation to appear on the web page // Wait for translation to appear on the web page
timer = 0; timer = 0;
while (!devtools->getTranslate() && timer < timer_stop) while (!devtools->checkMethod(target) && timer < timer_stop)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
timer += 0.1; timer += 0.1;
@ -138,7 +146,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
if (!(devtools->SendRequest("DOM.querySelector", { {"nodeId", docfound}, {"selector", "div.lmt__system_notification"} }, root)) if (!(devtools->SendRequest("DOM.querySelector", { {"nodeId", docfound}, {"selector", "div.lmt__system_notification"} }, root))
|| (root.value("result").toObject().value("nodeId").toInt() == 0)) || (root.value("result").toObject().value("nodeId").toInt() == 0))
{ {
return { false, FormatString(L"Error: result timeout %d s! %s", timer_stop, TRANSLATION_ERROR) }; return { false, FormatString(L"%s: %d ", ERROR_GOT_TIMEOUT, timer_stop) };
} }
noteNodeId = root.value("result").toObject().value("nodeId").toInt(); noteNodeId = root.value("result").toObject().value("nodeId").toInt();
@ -152,14 +160,13 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
{ {
OuterHTML = "Could not get notification"; OuterHTML = "Could not get notification";
} }
return { false, FormatString(L"Error: got notification from translator: %s", S(OuterHTML)) }; return { false, FormatString(L"%s", ERROR_COMMAND_FAIL) };
} }
// Catch the translation // Catch the translation
devtools->SendRequest("DOM.getOuterHTML", { {"nodeId", targetNodeId + 1} }, root); devtools->SendRequest("DOM.getOuterHTML", { {"nodeId", targetNodeId + 1} }, root);
result = root.value("result").toObject(); OuterHTML = root.value("result").toObject().value("outerHTML").toString();
OuterHTML = result.value("outerHTML").toString();
OuterHTML.remove(QRegExp("<[^>]*>")); OuterHTML.remove(QRegExp("<[^>]*>"));
OuterHTML = OuterHTML.trimmed(); OuterHTML = OuterHTML.trimmed();
@ -175,7 +182,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
QString targetlang = attributes[i + 1].toString().mid(0, 2); QString targetlang = attributes[i + 1].toString().mid(0, 2);
if (targetlang != S(translateTo.Copy())) if (targetlang != S(translateTo.Copy()))
{ {
return { false, FormatString(L"Error: target langs do not match (%s): %s", S(targetlang), S(OuterHTML)) }; return { false, FormatString(L"%s (%s): %s", ERROR_LANGUAGE, S(targetlang), S(OuterHTML)) };
} }
} }
} }
@ -190,6 +197,6 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devt
} }
else else
{ {
return { false, FormatString(L"Error: navigate failed! %s", TRANSLATION_ERROR) }; return { false, FormatString(L"%s", ERROR_COMMAND_FAIL) };
} }
} }