Bug 1748550 support persisted events for captivePortal r=rpl,robwu

Differential Revision: https://phabricator.services.mozilla.com/D136299
This commit is contained in:
Shane Caraveo 2022-02-26 20:42:59 +00:00
parent 8d79179a4c
commit d854f45476
2 changed files with 193 additions and 75 deletions

View File

@ -27,83 +27,126 @@ var { getSettingsAPI } = ExtensionPreferencesManager;
const CAPTIVE_URL_PREF = "captivedetect.canonicalURL";
function nameForCPSState(state) {
switch (state) {
case gCPS.UNKNOWN:
return "unknown";
case gCPS.NOT_CAPTIVE:
return "not_captive";
case gCPS.UNLOCKED_PORTAL:
return "unlocked_portal";
case gCPS.LOCKED_PORTAL:
return "locked_portal";
default:
return "unknown";
}
}
var { ExtensionError } = ExtensionUtils;
this.captivePortal = class extends ExtensionAPI {
getAPI(context) {
function checkEnabled() {
if (!gCaptivePortalEnabled) {
throw new ExtensionError("Captive Portal detection is not enabled");
}
checkCaptivePortalEnabled() {
if (!gCaptivePortalEnabled) {
throw new ExtensionError("Captive Portal detection is not enabled");
}
}
nameForCPSState(state) {
switch (state) {
case gCPS.UNKNOWN:
return "unknown";
case gCPS.NOT_CAPTIVE:
return "not_captive";
case gCPS.UNLOCKED_PORTAL:
return "unlocked_portal";
case gCPS.LOCKED_PORTAL:
return "locked_portal";
default:
return "unknown";
}
}
PERSISTENT_EVENTS = {
onStateChanged: fire => {
this.checkCaptivePortalEnabled();
let observer = (subject, topic) => {
fire.async({ state: this.nameForCPSState(gCPS.state) });
};
Services.obs.addObserver(
observer,
"ipc:network:captive-portal-set-state"
);
return {
unregister: () => {
Services.obs.removeObserver(
observer,
"ipc:network:captive-portal-set-state"
);
},
convert(_fire, context) {
fire = _fire;
},
};
},
onConnectivityAvailable: fire => {
this.checkCaptivePortalEnabled();
let observer = (subject, topic, data) => {
fire.async({ status: data });
};
Services.obs.addObserver(observer, "network:captive-portal-connectivity");
return {
unregister: () => {
Services.obs.removeObserver(
observer,
"network:captive-portal-connectivity"
);
},
convert(_fire, context) {
fire = _fire;
},
};
},
"captiveURL.onChange": fire => {
let listener = (text, id) => {
fire.async({
levelOfControl: "not_controllable",
value: Services.prefs.getStringPref(CAPTIVE_URL_PREF),
});
};
Services.prefs.addObserver(CAPTIVE_URL_PREF, listener);
return {
unregister: () => {
Services.prefs.removeObserver(CAPTIVE_URL_PREF, listener);
},
convert(_fire, context) {
fire = _fire;
},
};
},
};
primeListener(extension, event, fire) {
if (Object.hasOwn(this.PERSISTENT_EVENTS, event)) {
return this.PERSISTENT_EVENTS[event](fire);
}
}
getAPI(context) {
let self = this;
return {
captivePortal: {
getState() {
checkEnabled();
return nameForCPSState(gCPS.state);
self.checkCaptivePortalEnabled();
return self.nameForCPSState(gCPS.state);
},
getLastChecked() {
checkEnabled();
self.checkCaptivePortalEnabled();
return gCPS.lastChecked;
},
onStateChanged: new EventManager({
context,
name: "captivePortal.onStateChanged",
module: "captivePortal",
event: "onStateChanged",
register: fire => {
checkEnabled();
let observer = (subject, topic) => {
fire.async({ state: nameForCPSState(gCPS.state) });
};
Services.obs.addObserver(
observer,
"ipc:network:captive-portal-set-state"
);
return () => {
Services.obs.removeObserver(
observer,
"ipc:network:captive-portal-set-state"
);
};
return self.PERSISTENT_EVENTS.onStateChanged(fire).unregister;
},
}).api(),
onConnectivityAvailable: new EventManager({
context,
name: "captivePortal.onConnectivityAvailable",
module: "captivePortal",
event: "onConnectivityAvailable",
register: fire => {
checkEnabled();
let observer = (subject, topic, data) => {
fire.async({ status: data });
};
Services.obs.addObserver(
observer,
"network:captive-portal-connectivity"
);
return () => {
Services.obs.removeObserver(
observer,
"network:captive-portal-connectivity"
);
};
return self.PERSISTENT_EVENTS.onConnectivityAvailable(fire)
.unregister;
},
}).api(),
canonicalURL: getSettingsAPI({
@ -115,18 +158,11 @@ this.captivePortal = class extends ExtensionAPI {
readOnly: true,
onChange: new ExtensionCommon.EventManager({
context,
name: "captiveURL.onChange",
module: "captivePortal",
event: "captiveURL.onChange",
register: fire => {
let listener = (text, id) => {
fire.async({
levelOfControl: "not_controllable",
value: Services.prefs.getStringPref(CAPTIVE_URL_PREF),
});
};
Services.prefs.addObserver(CAPTIVE_URL_PREF, listener);
return () => {
Services.prefs.removeObserver(CAPTIVE_URL_PREF, listener);
};
return self.PERSISTENT_EVENTS["captiveURL.onChange"](fire)
.unregister;
},
}).api(),
}),

View File

@ -1,5 +1,19 @@
"use strict";
Services.prefs.setBoolPref(
"extensions.webextensions.background-delayed-startup",
true
);
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"1",
"43"
);
/**
* This duplicates the test from netwerk/test/unit/test_captive_portal_service.js
* however using an extension to gather the captive portal information.
@ -30,7 +44,7 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_DNS_NATIVE_IS_LOCALHOST);
});
add_task(function setup() {
add_task(async function setup() {
Services.prefs.setCharPref(
PREF_CAPTIVE_ENDPOINT,
`http://localhost:${httpserver.identity.primaryPort}/captive.txt`
@ -38,6 +52,9 @@ add_task(function setup() {
Services.prefs.setBoolPref(PREF_CAPTIVE_TESTMODE, true);
Services.prefs.setIntPref(PREF_CAPTIVE_MINTIME, 0);
Services.prefs.setBoolPref(PREF_DNS_NATIVE_IS_LOCALHOST, true);
Services.prefs.setBoolPref("extensions.eventPages.enabled", true);
await AddonTestUtils.promiseStartupManager();
});
add_task(async function test_captivePortal_basic() {
@ -46,8 +63,10 @@ add_task(async function test_captivePortal_basic() {
);
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
permissions: ["captivePortal"],
background: { persistent: false },
},
isPrivileged: true,
async background() {
@ -63,6 +82,10 @@ add_task(async function test_captivePortal_basic() {
browser.test.sendMessage("state", details);
});
browser.captivePortal.canonicalURL.onChange.addListener(details => {
browser.test.sendMessage("url", details);
});
browser.test.onMessage.addListener(async msg => {
if (msg == "getstate") {
browser.test.sendMessage(
@ -71,21 +94,20 @@ add_task(async function test_captivePortal_basic() {
);
}
});
browser.test.assertEq(
"unknown",
await browser.captivePortal.getState(),
"initial state unknown"
);
},
});
await extension.startup();
extension.sendMessage("getstate");
let details = await extension.awaitMessage("getstate");
equal(details, "unknown", "initial state");
// The captive portal service is started by nsIOService when the pref becomes true, so we
// toggle the pref. We cannot set to false before the extension loads above.
Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, true);
let details = await extension.awaitMessage("connectivity");
details = await extension.awaitMessage("connectivity");
equal(details.status, "clear", "initial connectivity");
extension.sendMessage("getstate");
details = await extension.awaitMessage("getstate");
@ -106,5 +128,65 @@ add_task(async function test_captivePortal_basic() {
details = await extension.awaitMessage("state");
equal(details.state, "unlocked_portal", "state after unlocking portal");
assertPersistentListeners(
extension,
"captivePortal",
"onConnectivityAvailable",
{
primed: false,
}
);
assertPersistentListeners(extension, "captivePortal", "onStateChanged", {
primed: false,
});
assertPersistentListeners(extension, "captivePortal", "captiveURL.onChange", {
primed: false,
});
info("Test event page terminate/waken");
await extension.terminateBackground();
assertPersistentListeners(extension, "captivePortal", "onStateChanged", {
primed: true,
});
assertPersistentListeners(
extension,
"captivePortal",
"onConnectivityAvailable",
{
primed: true,
}
);
assertPersistentListeners(extension, "captivePortal", "captiveURL.onChange", {
primed: true,
});
info("REFRESH 2nd pass to other");
cpResponse = "other";
cps.recheckCaptivePortal();
details = await extension.awaitMessage("state");
equal(details.state, "locked_portal", "state in portal");
info("Test event page terminate/waken with settings");
await extension.terminateBackground();
assertPersistentListeners(extension, "captivePortal", "captiveURL.onChange", {
primed: true,
});
Services.prefs.setStringPref(
"captivedetect.canonicalURL",
"http://example.com"
);
let url = await extension.awaitMessage("url");
equal(
url.value,
"http://example.com",
"The canonicalURL setting has the expected value."
);
await extension.unload();
});