mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
[UWP] Basic console panel
This commit is contained in:
parent
2560e78f11
commit
bd8c7d6f4d
9 changed files with 400 additions and 33 deletions
237
support/hololens/ServoApp/Devtools/Client.cpp
Normal file
237
support/hololens/ServoApp/Devtools/Client.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "pch.h"
|
||||
#include "strutils.h"
|
||||
#include "Client.h"
|
||||
|
||||
using namespace winrt::Windows::Data::Json;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Networking;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
|
||||
namespace winrt::servo {
|
||||
|
||||
void DevtoolsClient::Stop() {
|
||||
if (mReceiving && mReceiveOp.has_value() &&
|
||||
mReceiveOp->Status() != AsyncStatus::Completed) {
|
||||
mReceiveOp->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void DevtoolsClient::Run() {
|
||||
if (mReceiving) {
|
||||
throw hresult_error(E_FAIL, L"Already running");
|
||||
}
|
||||
mReceiving = true;
|
||||
auto socket = Sockets::StreamSocket();
|
||||
auto hostname = HostName(mHostname);
|
||||
auto connecting = socket.ConnectAsync(hostname, mPort);
|
||||
connecting.Completed([=](const auto &, const auto &) {
|
||||
mDataReader = DataReader(socket.InputStream());
|
||||
mDataWriter = DataWriter(socket.OutputStream());
|
||||
mReceiveOp = {Loop()};
|
||||
mReceiveOp->Completed([=](const auto &, const auto &) {
|
||||
mReceiveOp = {};
|
||||
mDataReader->DetachStream();
|
||||
mDataWriter->DetachStream();
|
||||
mReceiving = false;
|
||||
mDelegate.OnDevtoolsDetached();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
IAsyncAction DevtoolsClient::Loop() {
|
||||
auto cancellation = co_await winrt::get_cancellation_token();
|
||||
cancellation.callback([=] {
|
||||
if (mReaderOp.Status() != AsyncStatus::Completed) {
|
||||
mReaderOp.Cancel();
|
||||
}
|
||||
});
|
||||
|
||||
while (!cancellation()) {
|
||||
unsigned int len = 0;
|
||||
while (!cancellation()) {
|
||||
mReaderOp = mDataReader->LoadAsync(1);
|
||||
co_await mReaderOp;
|
||||
hstring c = mDataReader->ReadString(1);
|
||||
if (c == L":")
|
||||
break;
|
||||
try {
|
||||
unsigned int digit = std::stoi(c.c_str());
|
||||
len = 10 * len + digit;
|
||||
} catch (...) {
|
||||
throw hresult_error(E_FAIL, L"Can't parse message header:" + c);
|
||||
}
|
||||
if (len >= 10000) {
|
||||
throw hresult_error(E_FAIL, L"Message length too long");
|
||||
}
|
||||
}
|
||||
if (cancellation()) {
|
||||
break;
|
||||
}
|
||||
hstring request = L"";
|
||||
mReaderOp = mDataReader->LoadAsync(len);
|
||||
auto bytesLoaded = co_await mReaderOp;
|
||||
request = request + mDataReader->ReadString(bytesLoaded);
|
||||
JsonObject json;
|
||||
if (!JsonObject::TryParse(request, json)) {
|
||||
throw hresult_error(E_FAIL, L"Can't parse message: " + request);
|
||||
}
|
||||
HandleMessage(json);
|
||||
}
|
||||
}
|
||||
|
||||
void DevtoolsClient::HandleMessage(JsonObject obj) {
|
||||
// Basic devtools protocol implementation:
|
||||
// https://docs.firefox-dev.tools/backend/protocol.html
|
||||
|
||||
if (obj.HasKey(L"from") && obj.GetNamedString(L"from") == L"root") {
|
||||
if (obj.HasKey(L"applicationType")) {
|
||||
// First message. Ask for the current tab
|
||||
JsonObject out;
|
||||
out.Insert(L"to", JsonValue::CreateStringValue(L"root"));
|
||||
out.Insert(L"type", JsonValue::CreateStringValue(L"getTab"));
|
||||
Send(out);
|
||||
return;
|
||||
} else if (obj.HasKey(L"tab")) {
|
||||
// Got the current tab.
|
||||
auto tab = obj.GetNamedObject(L"tab");
|
||||
if (tab.HasKey(L"actor")) {
|
||||
// Attach to tab, and ask for cached messaged
|
||||
JsonObject msg1;
|
||||
msg1.Insert(L"to", tab.GetNamedValue(L"actor"));
|
||||
msg1.Insert(L"type", JsonValue::CreateStringValue(L"attach"));
|
||||
Send(msg1);
|
||||
JsonObject msg2;
|
||||
msg2.Insert(L"to", tab.GetNamedValue(L"consoleActor"));
|
||||
msg2.Insert(L"type",
|
||||
JsonValue::CreateStringValue(L"getCachedMessages"));
|
||||
JsonArray types;
|
||||
types.Append(JsonValue::CreateStringValue(L"PageError"));
|
||||
types.Append(JsonValue::CreateStringValue(L"ConsoleAPI"));
|
||||
msg2.Insert(L"messageTypes", types);
|
||||
Send(msg2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (obj.HasKey(L"type")) { // Not from root
|
||||
if (obj.GetNamedString(L"type") == L"pageError") {
|
||||
// Got a page error
|
||||
HandlePageError(obj.GetNamedObject(L"pageError"));
|
||||
return;
|
||||
} else if (obj.GetNamedString(L"type") == L"consoleAPICall") {
|
||||
// console.* calls
|
||||
auto message = obj.GetNamedObject(L"message");
|
||||
HandleConsoleMessage(message);
|
||||
return;
|
||||
} else if (obj.GetNamedString(L"type") == L"tabAttached") {
|
||||
// Ignore
|
||||
return;
|
||||
} else if (obj.GetNamedString(L"type") == L"networkEvent") {
|
||||
// Ignore
|
||||
return;
|
||||
} else if (obj.GetNamedString(L"type") == L"tabNavigated") {
|
||||
// Ignore
|
||||
return;
|
||||
} else if (obj.GetNamedString(L"type") == L"networkEventUpdate") {
|
||||
// FIXME: log if there is a non-200 HTTP response
|
||||
return;
|
||||
}
|
||||
} else if (obj.HasKey(L"messages")) {
|
||||
// Response to getCachedMessages
|
||||
for (auto messageValue : obj.GetNamedArray(L"messages")) {
|
||||
auto message = messageValue.GetObject();
|
||||
if (message.GetNamedString(L"_type") == L"ConsoleAPI") {
|
||||
HandleConsoleMessage(message);
|
||||
} else if (message.GetNamedString(L"_type") == L"PageError") {
|
||||
HandlePageError(message);
|
||||
} else {
|
||||
HandleNonHandledMessage(message);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
HandleNonHandledMessage(obj);
|
||||
}
|
||||
|
||||
DevtoolsMessageLevel DevtoolsClient::ParseLevel(JsonObject message) {
|
||||
if (message.GetNamedBoolean(L"error", false)) {
|
||||
return DevtoolsMessageLevel::Error;
|
||||
}
|
||||
if (message.GetNamedBoolean(L"warning", false)) {
|
||||
return DevtoolsMessageLevel::Warn;
|
||||
}
|
||||
if (message.GetNamedBoolean(L"exception", false)) {
|
||||
return DevtoolsMessageLevel::Error;
|
||||
}
|
||||
auto level = message.GetNamedString(L"level", L"");
|
||||
if (level == L"warn") {
|
||||
return DevtoolsMessageLevel::Warn;
|
||||
} else if (level == L"error") {
|
||||
return DevtoolsMessageLevel::Error;
|
||||
}
|
||||
return DevtoolsMessageLevel::None;
|
||||
}
|
||||
|
||||
hstring DevtoolsClient::ParseSource(JsonObject message) {
|
||||
auto source = message.GetNamedString(L"filename", L"<>");
|
||||
if (message.HasKey(L"lineNumber")) {
|
||||
source = source + L":" + to_hstring(message.GetNamedNumber(L"lineNumber"));
|
||||
}
|
||||
if (message.HasKey(L"columnNumber")) {
|
||||
source =
|
||||
source + L":" + to_hstring(message.GetNamedNumber(L"columnNumber"));
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
void DevtoolsClient::HandlePageError(JsonObject message) {
|
||||
auto source = ParseSource(message);
|
||||
auto body = message.GetNamedString(L"errorMessage", L"");
|
||||
auto level = ParseLevel(message);
|
||||
mDelegate.OnDevtoolsMessage(level, source, body);
|
||||
}
|
||||
|
||||
void DevtoolsClient::HandleConsoleMessage(JsonObject message) {
|
||||
auto source = ParseSource(message);
|
||||
auto level = ParseLevel(message);
|
||||
hstring body = L"";
|
||||
for (auto arg : message.GetNamedArray(L"arguments")) {
|
||||
body = body + arg.Stringify();
|
||||
}
|
||||
mDelegate.OnDevtoolsMessage(level, source, body);
|
||||
}
|
||||
|
||||
void DevtoolsClient::HandleNonHandledMessage(JsonObject message) {
|
||||
auto level = DevtoolsMessageLevel::Warn;
|
||||
auto body = L"Unhandled devtools message: " + message.Stringify();
|
||||
mDelegate.OnDevtoolsMessage(level, L"", body);
|
||||
}
|
||||
|
||||
void DevtoolsClient::SendPendingObjects() {
|
||||
if (mPendingObjects.empty() || mSending) {
|
||||
return;
|
||||
}
|
||||
mSending = true;
|
||||
auto obj = mPendingObjects.front();
|
||||
mPendingObjects.erase(mPendingObjects.begin());
|
||||
hstring msg = obj.Stringify();
|
||||
hstring size = to_hstring(msg.size());
|
||||
hstring request = size + L":" + msg;
|
||||
mDataWriter->WriteString(request);
|
||||
mDataWriter->StoreAsync().Completed([=](const auto &, const auto &) {
|
||||
mDataWriter->FlushAsync().Completed([=](const auto &, const auto &) {
|
||||
mSending = false;
|
||||
SendPendingObjects();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void DevtoolsClient::Send(JsonObject obj) {
|
||||
mPendingObjects.push_back(obj);
|
||||
SendPendingObjects();
|
||||
}
|
||||
|
||||
} // namespace winrt::servo
|
Loading…
Add table
Add a link
Reference in a new issue