2020-10-14 03:37:03 +03:00
|
|
|
#include "devtools.h"
|
|
|
|
|
|
|
|
DevTools::DevTools(QObject* parent) :
|
|
|
|
QObject(parent),
|
2020-11-04 01:23:20 +03:00
|
|
|
idCounter(0),
|
|
|
|
idMethod(0),
|
2020-10-14 03:37:03 +03:00
|
|
|
status("Stopped"),
|
2020-10-15 02:32:58 +03:00
|
|
|
session(0)
|
2020-10-15 23:23:41 +03:00
|
|
|
{
|
2020-10-29 02:08:11 +03:00
|
|
|
connect(&webSocket, &QWebSocket::stateChanged, this, &DevTools::stateChanged);
|
|
|
|
connect(&webSocket, &QWebSocket::textMessageReceived, this, &DevTools::onTextMessageReceived);
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void DevTools::startDevTools(QString path, bool headless, int port)
|
|
|
|
{
|
|
|
|
if (startChrome(path, headless, port))
|
|
|
|
{
|
2020-10-18 16:16:06 +03:00
|
|
|
QJsonDocument doc;
|
2020-10-14 03:37:03 +03:00
|
|
|
QString webSocketDebuggerUrl;
|
2020-10-18 16:16:06 +03:00
|
|
|
if (GetJsonfromHTTP(doc, "/json/list", port))
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
2020-10-18 16:16:06 +03:00
|
|
|
for (const auto obj : doc.array())
|
|
|
|
if (obj.toObject().value("type") == "page")
|
|
|
|
{
|
|
|
|
webSocketDebuggerUrl.append(obj.toObject().value("webSocketDebuggerUrl").toString());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!webSocketDebuggerUrl.isEmpty())
|
|
|
|
{
|
|
|
|
if (GetJsonfromHTTP(doc, "/json/version", port))
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
userAgent = doc.object().value("User-Agent").toString();
|
2020-10-18 16:16:06 +03:00
|
|
|
}
|
|
|
|
webSocket.open(webSocketDebuggerUrl);
|
|
|
|
session += 1;
|
|
|
|
return;
|
|
|
|
}
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
2020-11-04 01:23:20 +03:00
|
|
|
status = "Failed to find Chrome debug port!";
|
2020-10-18 16:16:06 +03:00
|
|
|
emit statusChanged(status);
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
status = "Failed to start Chrome!";
|
2020-10-14 03:37:03 +03:00
|
|
|
emit statusChanged(status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int DevTools::getSession()
|
|
|
|
{
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString DevTools::getStatus()
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2020-10-18 16:16:06 +03:00
|
|
|
QString DevTools::getUserAgent()
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
return userAgent;
|
2020-10-18 16:16:06 +03:00
|
|
|
}
|
|
|
|
|
2020-10-14 03:37:03 +03:00
|
|
|
DevTools::~DevTools()
|
|
|
|
{
|
|
|
|
closeDevTools();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevTools::startChrome(QString path, bool headless, int port)
|
|
|
|
{
|
|
|
|
if (!std::filesystem::exists(path.toStdWString()))
|
|
|
|
return false;
|
|
|
|
DWORD exitCode = 0;
|
2020-10-20 02:08:59 +03:00
|
|
|
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) != FALSE && exitCode == STILL_ACTIVE)
|
2020-10-14 03:37:03 +03:00
|
|
|
return false;
|
2020-10-15 23:23:41 +03:00
|
|
|
QString args = "--proxy-server=direct:// --disable-extensions --disable-gpu --user-data-dir="
|
2020-10-14 03:37:03 +03:00
|
|
|
+ QString::fromStdWString(std::filesystem::current_path())
|
2020-10-15 23:23:41 +03:00
|
|
|
+ "\\devtoolscache --remote-debugging-port="
|
2020-10-14 03:37:03 +03:00
|
|
|
+ QString::number(port);
|
|
|
|
if (headless)
|
|
|
|
args += " --headless";
|
|
|
|
STARTUPINFOW dummy = { sizeof(dummy) };
|
2020-10-15 23:23:41 +03:00
|
|
|
if (!CreateProcessW(NULL, (wchar_t*)(path + " " + args).utf16(), nullptr, nullptr,
|
2020-10-14 03:37:03 +03:00
|
|
|
FALSE, 0, nullptr, nullptr, &dummy, &processInfo))
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-18 16:16:06 +03:00
|
|
|
bool DevTools::GetJsonfromHTTP(QJsonDocument& doc, QString object, int port)
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
|
|
|
if (HttpRequest httpRequest{
|
|
|
|
L"Mozilla/5.0 Textractor",
|
|
|
|
L"127.0.0.1",
|
|
|
|
L"POST",
|
2020-10-18 16:16:06 +03:00
|
|
|
object.toStdWString().c_str(),
|
2020-10-14 03:37:03 +03:00
|
|
|
"",
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
WINHTTP_FLAG_ESCAPE_DISABLE,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
DWORD(port)
|
|
|
|
})
|
|
|
|
{
|
|
|
|
QString qtString = QString::fromStdWString(httpRequest.response);
|
2020-10-18 16:16:06 +03:00
|
|
|
doc = QJsonDocument::fromJson(qtString.toUtf8());
|
|
|
|
if (!doc.isEmpty())
|
2020-10-14 03:37:03 +03:00
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DevTools::stateChanged(QAbstractSocket::SocketState state)
|
|
|
|
{
|
|
|
|
QMetaEnum metaenum = QMetaEnum::fromType<QAbstractSocket::SocketState>();
|
|
|
|
status = metaenum.valueToKey(state);
|
|
|
|
emit statusChanged(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevTools::SendRequest(QString method, QJsonObject params, QJsonObject& root)
|
|
|
|
{
|
|
|
|
if (!isConnected())
|
|
|
|
return false;
|
|
|
|
root = QJsonObject();
|
|
|
|
QJsonObject json;
|
|
|
|
task_completion_event<QJsonObject> response;
|
|
|
|
long id = idIncrement();
|
|
|
|
json.insert("id", id);
|
|
|
|
json.insert("method", method);
|
2020-10-15 23:23:41 +03:00
|
|
|
json.insert("params", params);
|
2020-10-14 03:37:03 +03:00
|
|
|
QJsonDocument doc(json);
|
|
|
|
QString message(doc.toJson(QJsonDocument::Compact));
|
|
|
|
mutex.lock();
|
2020-11-04 01:23:20 +03:00
|
|
|
mapQueue.insert(std::make_pair(id, response));
|
2020-10-14 03:37:03 +03:00
|
|
|
mutex.unlock();
|
|
|
|
webSocket.sendTextMessage(message);
|
|
|
|
webSocket.flush();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
root = create_task(response).get();
|
|
|
|
}
|
|
|
|
catch (const std::exception& ex)
|
|
|
|
{
|
|
|
|
response.set_exception(ex);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!root.isEmpty())
|
|
|
|
{
|
|
|
|
if (root.contains("error"))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (root.contains("result"))
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:32:58 +03:00
|
|
|
long DevTools::methodToReceive(QString method, QJsonObject params)
|
|
|
|
{
|
|
|
|
QJsonObject json;
|
|
|
|
long id = idmIncrement();
|
|
|
|
json.insert("method", method);
|
|
|
|
json.insert("params", params);
|
|
|
|
mutex.lock();
|
2020-11-04 01:23:20 +03:00
|
|
|
mapMethod.insert(std::make_pair(id, json));
|
2020-10-15 02:32:58 +03:00
|
|
|
mutex.unlock();
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2020-10-14 03:37:03 +03:00
|
|
|
long DevTools::idIncrement()
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
return ++idCounter;
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
|
|
|
|
2020-10-15 02:32:58 +03:00
|
|
|
long DevTools::idmIncrement()
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
return ++idMethod;
|
2020-10-15 02:32:58 +03:00
|
|
|
}
|
|
|
|
|
2020-10-14 03:37:03 +03:00
|
|
|
bool DevTools::isConnected()
|
|
|
|
{
|
|
|
|
if (webSocket.state() == QAbstractSocket::ConnectedState)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-04 01:23:20 +03:00
|
|
|
bool DevTools::compareJson(QJsonValue stored, QJsonValue params)
|
2020-10-15 02:32:58 +03:00
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
if (stored.isObject())
|
2020-10-15 02:32:58 +03:00
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
foreach(const QString & key, stored.toObject().keys())
|
2020-10-15 23:23:41 +03:00
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
QJsonValue storedvalue = stored.toObject().value(key);
|
2020-10-15 23:23:41 +03:00
|
|
|
QJsonValue value = params.toObject().value(key);
|
|
|
|
if (!compareJson(storedvalue, value))
|
|
|
|
return false;
|
|
|
|
}
|
2020-10-15 02:32:58 +03:00
|
|
|
}
|
2020-11-04 01:23:20 +03:00
|
|
|
else if (stored.isArray())
|
2020-10-15 23:23:41 +03:00
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
for (int i = 0; i < stored.toArray().size(); i++)
|
|
|
|
if (!compareJson(stored.toArray()[i], params.toArray()[i]))
|
2020-10-15 23:23:41 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-11-04 01:23:20 +03:00
|
|
|
else if (stored.isString())
|
|
|
|
{
|
|
|
|
if (!stored.toString().contains(params.toString()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (stored.toVariant() != params.toVariant())
|
2020-10-15 23:23:41 +03:00
|
|
|
return false;
|
|
|
|
|
2020-10-15 02:32:58 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevTools::checkMethod(long id)
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
MapMethod::iterator iter = mapMethod.find(id);
|
|
|
|
if (iter == mapMethod.end())
|
2020-10-15 02:32:58 +03:00
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-14 03:37:03 +03:00
|
|
|
void DevTools::onTextMessageReceived(QString message)
|
|
|
|
{
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
|
|
|
|
if (doc.isObject())
|
|
|
|
{
|
|
|
|
QJsonObject root = doc.object();
|
|
|
|
if (root.contains("method"))
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
for (auto iter = mapMethod.cbegin(); iter != mapMethod.cend();)
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
2020-10-20 02:08:59 +03:00
|
|
|
if (iter->second.value("method") == root.value("method")
|
|
|
|
&& compareJson(iter->second.value("params"), root.value("params")))
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
|
|
|
mutex.lock();
|
2020-11-04 01:23:20 +03:00
|
|
|
mapMethod.erase(iter++);
|
2020-10-14 03:37:03 +03:00
|
|
|
mutex.unlock();
|
|
|
|
}
|
2020-10-15 02:32:58 +03:00
|
|
|
++iter;
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (root.contains("id"))
|
|
|
|
{
|
|
|
|
long id = root.value("id").toInt();
|
2020-11-04 01:23:20 +03:00
|
|
|
MapResponse::iterator request = mapQueue.find(id);
|
|
|
|
if (request != mapQueue.end())
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
|
|
|
request->second.set(root);
|
|
|
|
mutex.lock();
|
2020-11-04 01:23:20 +03:00
|
|
|
mapQueue.erase(request);
|
2020-10-14 03:37:03 +03:00
|
|
|
mutex.unlock();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2020-10-15 23:23:41 +03:00
|
|
|
}
|
2020-10-14 03:37:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void DevTools::closeDevTools()
|
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
if (this->mapQueue.size() > 0)
|
2020-10-14 03:37:03 +03:00
|
|
|
{
|
2020-11-04 01:23:20 +03:00
|
|
|
MapResponse::iterator iter = mapQueue.begin();
|
|
|
|
MapResponse::iterator iend = mapQueue.end();
|
2020-10-14 03:37:03 +03:00
|
|
|
for (; iter != iend; iter++)
|
|
|
|
{
|
|
|
|
iter->second.set_exception("exception");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
webSocket.close();
|
2020-11-04 01:23:20 +03:00
|
|
|
mapMethod.clear();
|
|
|
|
mapQueue.clear();
|
|
|
|
idCounter = 0;
|
|
|
|
idMethod = 0;
|
2020-10-14 03:37:03 +03:00
|
|
|
DWORD exitCode = 0;
|
|
|
|
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) != FALSE)
|
|
|
|
{
|
|
|
|
if (exitCode == STILL_ACTIVE)
|
|
|
|
{
|
|
|
|
TerminateProcess(processInfo.hProcess, 0);
|
|
|
|
WaitForSingleObject(processInfo.hProcess, 100);
|
|
|
|
CloseHandle(processInfo.hProcess);
|
|
|
|
CloseHandle(processInfo.hThread);
|
|
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::filesystem::remove_all(L"devtoolscache");
|
|
|
|
}
|
|
|
|
catch (const std::exception&)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
status = "Stopped";
|
|
|
|
emit statusChanged(status);
|
2020-10-15 23:23:41 +03:00
|
|
|
}
|