Skip to content

Commit

Permalink
Allow sending settings to the web-ext (#10112)
Browse files Browse the repository at this point in the history
* Allow sending settings to the web-ext

* Export makeMessage

* Fix test, actually send the correct blob
  • Loading branch information
strseb authored Dec 10, 2024
1 parent 144b314 commit b12cdfd
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 113 deletions.
215 changes: 119 additions & 96 deletions src/webextensionadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "webextensionadapter.h"

#include <QFileInfo>
#include <QHostAddress>
#include <QJsonArray>
#include <QJsonDocument>
Expand Down Expand Up @@ -73,104 +74,112 @@ WebExtensionAdapter::WebExtensionAdapter(QObject* parent)
connect(vpn->connectionHealth(), &ConnectionHealth::stabilityChanged, this,
&WebExtensionAdapter::writeState);

m_commands = QList<RequestType>(
{RequestType{"activate",
[](const QJsonObject&) {
auto t = new TaskControllerAction(
TaskControllerAction::eActivateForExtension);
TaskScheduler::scheduleTask(t);
QJsonObject obj;
obj["ok"] = true;
return QJsonObject();
}},
RequestType{"deactivate",
[](const QJsonObject&) {
auto t = new TaskControllerAction(
TaskControllerAction::eDeactivateForExtension);
TaskScheduler::scheduleTask(t);
QJsonObject obj;
obj["ok"] = true;
return QJsonObject();
}},
RequestType{"servers",
[this](const QJsonObject&) {
QJsonObject servers;
serializeServerCountry(
MozillaVPN::instance()->serverCountryModel(), servers);

QJsonObject obj;
obj["servers"] = servers;
return obj;
}},
RequestType{"focus",
[](const QJsonObject&) {
QmlEngineHolder* engine = QmlEngineHolder::instance();
engine->showWindow();
return QJsonObject{};
}},
RequestType{"openAuth",
[](const QJsonObject&) {
MozillaVPN* vpn = MozillaVPN::instance();
if (vpn->state() != MozillaVPN::StateInitialize) {
return QJsonObject{};
}
vpn->authenticate();
return QJsonObject{};
}},
RequestType{"disabled_apps",
[](const QJsonObject&) {
QJsonArray apps;
for (const QString& app :
SettingsHolder::instance()->vpnDisabledApps()) {
apps.append(app);
}

QJsonObject obj;
obj["disabled_apps"] = apps;
return obj;
}},
RequestType{"featurelist",
[this](const QJsonObject&) {
QJsonObject obj;
obj["featurelist"] = serializeFeaturelist();
return obj;
}},

RequestType{"status",
[this](const QJsonObject&) {
QJsonObject obj;
obj["status"] = serializeStatus();
return obj;
}},
RequestType{"telemetry",
[](const QJsonObject& data) {
auto info = WebextensionTelemetry::fromJson(data);
if (info.has_value()) {
WebextensionTelemetry::recordTelemetry(info.value());
}
return QJsonObject{};
}},
RequestType{"session_start",
[](const QJsonObject& data) {
WebextensionTelemetry::startSession();
return QJsonObject{};
}},
RequestType{"session_stop",
[](const QJsonObject& data) {
WebextensionTelemetry::stopSession();
return QJsonObject{};
}},
RequestType{"interventions", [](const QJsonObject&) {
QJsonObject out;
QJsonArray interventions;
m_commands = QList<RequestType>({
RequestType{"activate",
[](const QJsonObject&) {
auto t = new TaskControllerAction(
TaskControllerAction::eActivateForExtension);
TaskScheduler::scheduleTask(t);
QJsonObject obj;
obj["ok"] = true;
return QJsonObject();
}},
RequestType{"deactivate",
[](const QJsonObject&) {
auto t = new TaskControllerAction(
TaskControllerAction::eDeactivateForExtension);
TaskScheduler::scheduleTask(t);
QJsonObject obj;
obj["ok"] = true;
return QJsonObject();
}},
RequestType{"servers",
[this](const QJsonObject&) {
QJsonObject servers;
serializeServerCountry(
MozillaVPN::instance()->serverCountryModel(), servers);

QJsonObject obj;
obj["servers"] = servers;
return obj;
}},
RequestType{"focus",
[](const QJsonObject&) {
QmlEngineHolder* engine = QmlEngineHolder::instance();
engine->showWindow();
return QJsonObject{};
}},
RequestType{"openAuth",
[](const QJsonObject&) {
MozillaVPN* vpn = MozillaVPN::instance();
if (vpn->state() != MozillaVPN::StateInitialize) {
return QJsonObject{};
}
vpn->authenticate();
return QJsonObject{};
}},
RequestType{"disabled_apps",
[](const QJsonObject&) {
QJsonArray apps;
for (const QString& app :
SettingsHolder::instance()->vpnDisabledApps()) {
apps.append(app);
}

QJsonObject obj;
obj["disabled_apps"] = apps;
return obj;
}},
RequestType{"featurelist",
[this](const QJsonObject&) {
QJsonObject obj;
obj["featurelist"] = serializeFeaturelist();
return obj;
}},
RequestType{"status",
[this](const QJsonObject&) {
QJsonObject obj;
obj["status"] = serializeStatus();
return obj;
}},
RequestType{"telemetry",
[](const QJsonObject& data) {
auto info = WebextensionTelemetry::fromJson(data);
if (info.has_value()) {
WebextensionTelemetry::recordTelemetry(info.value());
}
return QJsonObject{};
}},
RequestType{"session_start",
[](const QJsonObject& data) {
WebextensionTelemetry::startSession();
return QJsonObject{};
}},
RequestType{"session_stop",
[](const QJsonObject& data) {
WebextensionTelemetry::stopSession();
return QJsonObject{};
}},
RequestType{"interventions",
[](const QJsonObject&) {
QJsonObject out;
QJsonArray interventions;
#ifdef MZ_WINDOWS
if (Intervention::KillerNetwork::systemAffected()) {
interventions.append(Intervention::KillerNetwork::id);
}
if (Intervention::KillerNetwork::systemAffected()) {
interventions.append(Intervention::KillerNetwork::id);
}
#endif
out["interventions"] = interventions;
return out;
}}});
out["interventions"] = interventions;
return out;
}},
RequestType{"settings",
[this](const QJsonObject& data) {
if (data["settings"].isObject()) {
applySettings(data["settings"].toObject());
}
return QJsonObject{{"settings", serializeSettings()}};
}},
});
}

WebExtensionAdapter::~WebExtensionAdapter() {
Expand Down Expand Up @@ -290,3 +299,17 @@ void WebExtensionAdapter::serializeServerCountry(ServerCountryModel* model,

obj["countries"] = countries;
}

QJsonObject WebExtensionAdapter::serializeSettings() {
auto const settings = SettingsHolder::instance();
return {{"extensionTelemetryEnabled", settings->extensionTelemetryEnabled()}};
}

void WebExtensionAdapter::applySettings(const QJsonObject& data) {
auto const settings = SettingsHolder::instance();

auto enabled = data["extensionTelemetryEnabled"];
if (enabled.isBool()) {
settings->setExtensionTelemetryEnabled(enabled.toBool());
}
}
4 changes: 4 additions & 0 deletions src/webextensionadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class WebExtensionAdapter : public WebExtension::BaseAdapter {
QJsonObject serializeStatus();
QJsonObject serializeFeaturelist();
void serializeServerCountry(ServerCountryModel* model, QJsonObject& obj);

QJsonObject serializeSettings();
void applySettings(const QJsonObject& data);

QPropertyObserver mProxyStateChanged;
};

Expand Down
44 changes: 33 additions & 11 deletions tests/functional/testWebExtensionApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
connectExtension,
getMessageStream,
ExtensionMessage,
makeMessage,
readResponseOfType
} = require('./utils/webextension.js');

Expand All @@ -29,7 +30,7 @@ describe('WebExtension API', function() {
const sock = await connectExtension();
const messagePipe = getMessageStream(sock);
const statusPromise = readResponseOfType('status', messagePipe);
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await statusPromise
assert(msg.status.version, `A Version is sent in msg: ${JSON.stringify(msg)}` )
assert(msg.status.connectionHealth, `The current Connection Health status is sent in msg: ${JSON.stringify(msg)}` )
Expand All @@ -38,10 +39,10 @@ describe('WebExtension API', function() {
it('A Webextension can activate the VPN', async () => {
const sock = await connectExtension();
const messagePipe = getMessageStream(sock);
sentToClient(new ExtensionMessage('activate'), sock);
sentToClient(makeMessage('activate'), sock);

await vpn.waitForCondition(async () => {
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await readResponseOfType('status', messagePipe);
return msg.status.vpn === 'StateOnPartial';
});
Expand All @@ -53,16 +54,16 @@ describe('WebExtension API', function() {
it('A Webextension can deactivate the VPN if it Self Activated', async () => {
const sock = await connectExtension();
const messagePipe = getMessageStream(sock);
sentToClient(new ExtensionMessage('activate'), sock);
sentToClient(makeMessage('activate'), sock);
await readResponseOfType('activate', messagePipe);
await vpn.waitForCondition(async () => {
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await readResponseOfType('status', messagePipe);
return msg.status.vpn === 'StateOnPartial';
});
sentToClient(new ExtensionMessage('deactivate'), sock);
sentToClient(makeMessage('deactivate'), sock);
await vpn.waitForCondition(async () => {
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await readResponseOfType('status', messagePipe);
return msg.status.vpn === 'StateOff';
});
Expand All @@ -81,15 +82,15 @@ describe('WebExtension API', function() {
const messagePipe = getMessageStream(sock);

await vpn.waitForCondition(async () => {
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await readResponseOfType('status', messagePipe);
return msg.status.vpn === 'StateOn';
}, 500, 'VPN should report StateOn');
sentToClient(new ExtensionMessage('deactivate'), sock);
sentToClient(makeMessage('deactivate'), sock);
await vpn.wait(200);

await vpn.waitForCondition(async () => {
sentToClient(new ExtensionMessage('status'), sock);
sentToClient(makeMessage('status'), sock);
const msg = await readResponseOfType('status', messagePipe);
return msg.status.vpn === 'StateOn';
}, 500, 'VPN should still report StateOn');
Expand All @@ -101,7 +102,7 @@ describe('WebExtension API', function() {
const messagePipe = getMessageStream(sock);

const response = readResponseOfType('featurelist', messagePipe);
sentToClient(new ExtensionMessage('featurelist'), sock);
sentToClient(makeMessage('featurelist'), sock);
const msg = await response;
assert(msg.featurelist.webExtension === true);
assert(msg.featurelist.hasOwnProperty('localProxy'));
Expand All @@ -117,6 +118,27 @@ describe('WebExtension API', function() {
assert(msg.status.connectionHealth == "Unstable", "The extension was notified of the instability: "+ msg.status.connectionHealth)
sock.destroy();
});

it('A Webextension can read settings', async () => {
const sock = await connectExtension();
const messagePipe = getMessageStream(sock);
const settingsPromise = readResponseOfType('settings', messagePipe);
sentToClient(makeMessage('settings'), sock);
const msg = await settingsPromise
assert(Object.keys(msg.settings).length != 0, "The Extension was sent setting values");
sock.destroy();
});
it('A Webextension can write settings', async () => {
const sock = await connectExtension();
const messagePipe = getMessageStream(sock);
const settingsPromise = readResponseOfType('settings', messagePipe);
sentToClient(makeMessage('settings', {
settings: {"extensionTelemetryEnabled":true}
}), sock);
const msg = await settingsPromise
assert(msg.settings.extensionTelemetryEnabled, "The Extension was able to set the TelemetrySetting");
sock.destroy();
});
});

}
15 changes: 9 additions & 6 deletions tests/functional/utils/webextension.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ const {Buffer} = require('node:buffer');

const SERVER_PORT = 8754;

class ExtensionMessage{
t= ""
}

function ExtensionMessage(aT) {
if (!aT) {
this.t = 'help'
return;
function makeMessage(aT, extra) {
return {
...extra,
t: aT
}
this.t = aT
};

/**
Expand Down Expand Up @@ -98,5 +100,6 @@ module.exports = {
sentToClient,
connectExtension,
ExtensionMessage,
readResponseOfType
readResponseOfType,
makeMessage,
}

0 comments on commit b12cdfd

Please sign in to comment.