Backed out 4 changesets (bug 1440022) for nsSocketTransport crashes when trying to access the internet CLOSED TREE

Backed out changeset 9fa127e6df56 (bug 1440022)
Backed out changeset 4f84a6745a51 (bug 1440022)
Backed out changeset 00cf71bb3861 (bug 1440022)
Backed out changeset b7d5b0db60ad (bug 1440022)
This commit is contained in:
Bogdan Tara 2018-06-11 22:17:53 +03:00
parent 373f29fd9d
commit 3719c11a02
12 changed files with 14 additions and 614 deletions

View File

@ -1,216 +0,0 @@
/* jshint moz: true, esnext: true */
/* 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 http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "JSONFile", "resource://gre/modules/JSONFile.jsm");
var EXPORTED_SYMBOLS = ["pushBroadcastService"];
// We are supposed to ignore any updates with this version.
// FIXME: what is the actual "dummy" version?
// See bug 1467550.
const DUMMY_VERSION_STRING = "dummy";
XPCOMUtils.defineLazyGetter(this, "console", () => {
let {ConsoleAPI} = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
maxLogLevelPref: "dom.push.loglevel",
prefix: "BroadcastService",
});
});
ChromeUtils.defineModuleGetter(this, "PushService", "resource://gre/modules/PushService.jsm");
class InvalidSourceInfo extends Error {
constructor(message) {
super(message);
this.name = 'InvalidSourceInfo';
}
}
const BROADCAST_SERVICE_VERSION = 1;
var BroadcastService = class {
constructor(pushService, path) {
this.pushService = pushService;
this.jsonFile = new JSONFile({
path,
dataPostProcessor: this._initializeJSONFile,
});
this.initializePromise = this.jsonFile.load();
}
/**
* Convert the listeners from our on-disk format to the format
* needed by a hello message.
*/
async getListeners() {
await this.initializePromise;
return Object.entries(this.jsonFile.data.listeners).reduce((acc, [k, v]) => {
acc[k] = v.version;
return acc;
}, {});
}
_initializeJSONFile(data) {
if (!data.version) {
data.version = BROADCAST_SERVICE_VERSION;
}
if (!data.hasOwnProperty("listeners")) {
data.listeners = {};
}
return data;
}
/**
* Reset to a state akin to what you would get in a new profile.
* In particular, wipe anything from storage.
*
* Used mainly for testing.
*/
async _resetListeners() {
await this.initializePromise;
this.jsonFile.data = this._initializeJSONFile({});
this.initializePromise = Promise.resolve();
}
/**
* Ensure that a sourceInfo is correct (has the expected fields).
*/
_validateSourceInfo(sourceInfo) {
const {moduleURI, symbolName} = sourceInfo;
if (typeof moduleURI !== "string") {
throw new InvalidSourceInfo(`moduleURI must be a string (got ${typeof moduleURI})`);
}
if (typeof symbolName !== "string") {
throw new InvalidSourceInfo(`symbolName must be a string (got ${typeof symbolName})`);
}
}
/**
* Add an entry for a given listener if it isn't present, or update
* one if it is already present.
*
* Note that this means only a single listener can be set for a
* given subscription. This is a limitation in the current API that
* stems from the fact that there can only be one source of truth
* for the subscriber's version. As a workaround, you can define a
* listener which calls multiple other listeners.
*
* @param {string} broadcastId The broadcastID to listen for
* @param {string} version The most recent version we have for
* updates from this broadcastID
* @param {Object} sourceInfo A description of the handler for
* updates on this broadcastID
*/
async addListener(broadcastId, version, sourceInfo) {
console.info("addListener: adding listener", broadcastId, version, sourceInfo);
await this.initializePromise;
this._validateSourceInfo(sourceInfo);
if (typeof version !== "string") {
throw new TypeError("version should be a string");
}
const isNew = !this.jsonFile.data.listeners.hasOwnProperty(broadcastId);
// Update listeners before telling the pushService to subscribe,
// in case it would disregard the update in the small window
// between getting listeners and setting state to RUNNING.
this.jsonFile.data.listeners[broadcastId] = {version, sourceInfo};
this.jsonFile.saveSoon();
if (isNew) {
await this.pushService.subscribeBroadcast(broadcastId, version);
}
}
async receivedBroadcastMessage(broadcasts) {
console.info("receivedBroadcastMessage:", broadcasts);
await this.initializePromise;
for (const broadcastId in broadcasts) {
const version = broadcasts[broadcastId];
if (version === DUMMY_VERSION_STRING) {
console.info("Ignoring", version, "because it's the dummy version");
continue;
}
// We don't know this broadcastID. This is probably a bug?
if (!this.jsonFile.data.listeners.hasOwnProperty(broadcastId)) {
console.warn("receivedBroadcastMessage: unknown broadcastId", broadcastId);
continue;
}
const {sourceInfo} = this.jsonFile.data.listeners[broadcastId];
try {
this._validateSourceInfo(sourceInfo);
} catch (e) {
console.error("receivedBroadcastMessage: malformed sourceInfo", sourceInfo, e);
continue;
}
const {moduleURI, symbolName} = sourceInfo;
const module = {};
try {
ChromeUtils.import(moduleURI, module);
} catch (e) {
console.error("receivedBroadcastMessage: couldn't invoke", broadcastId,
"because import of module", moduleURI,
"failed", e);
continue;
}
if (!module[symbolName]) {
console.error("receivedBroadcastMessage: couldn't invoke", broadcastId,
"because module", moduleName, "missing attribute", symbolName);
continue;
}
const handler = module[symbolName];
if (!handler.receivedBroadcastMessage) {
console.error("receivedBroadcastMessage: couldn't invoke", broadcastId,
"because handler returned by", `${moduleURI}.${symbolName}`,
"has no receivedBroadcastMessage method");
continue;
}
try {
await handler.receivedBroadcastMessage(version, broadcastId);
} catch (e) {
console.error("receivedBroadcastMessage: handler for", broadcastId,
"threw error:", e);
continue;
}
// Broadcast message applied successfully. Update the version we
// received if it's different than the one we had. We don't
// enforce an ordering here (i.e. we use != instead of <)
// because we don't know what the ordering of the service's
// versions is going to be.
if (this.jsonFile.data.listeners[broadcastId].version != version) {
this.jsonFile.data.listeners[broadcastId].version = version;
this.jsonFile.saveSoon();
}
}
}
// For test only.
_saveImmediately() {
return this.jsonFile._save();
}
}
function initializeBroadcastService() {
// Fallback path for xpcshell tests.
let path = "broadcast-listeners.json";
if (OS.Constants.Path.profileDir) {
// Real path for use in a real profile.
path = OS.Path.join(OS.Constants.Path.profileDir, path);
}
return new BroadcastService(PushService, path);
};
var pushBroadcastService = initializeBroadcastService();

View File

@ -32,7 +32,6 @@ const CONNECTION_PROTOCOLS = (function() {
XPCOMUtils.defineLazyServiceGetter(this, "gPushNotifier",
"@mozilla.org/push/Notifier;1",
"nsIPushNotifier");
ChromeUtils.defineModuleGetter(this, "pushBroadcastService", "resource://gre/modules/PushBroadcastService.jsm");
var EXPORTED_SYMBOLS = ["PushService"];
@ -241,19 +240,14 @@ var PushService = {
}
let records = await this.getAllUnexpired();
let broadcastListeners = await pushBroadcastService.getListeners();
// In principle, a listener could be added to the
// pushBroadcastService here, after we have gotten listeners and
// before we're RUNNING, but this can't happen in practice because
// the only caller that can add listeners is PushBroadcastService,
// and it waits on the same promise we are before it can add
// listeners. If PushBroadcastService gets woken first, it will
// update the value that is eventually returned from
// getListeners.
this._setState(PUSH_SERVICE_RUNNING);
this._service.connect(records, broadcastListeners);
if (records.length > 0 || prefs.get("alwaysConnect")) {
// Connect if we have existing subscriptions, or if the always-on pref
// is set.
this._service.connect(records);
}
},
_changeStateConnectionEnabledEvent: function(enabled) {
@ -492,13 +486,13 @@ var PushService = {
if (options.serverURI) {
// this is use for xpcshell test.
return this._stateChangeProcessEnqueue(_ =>
this._stateChangeProcessEnqueue(_ =>
this._changeServerURL(options.serverURI, STARTING_SERVICE_EVENT, options));
} else {
// This is only used for testing. Different tests require connecting to
// slightly different URLs.
return this._stateChangeProcessEnqueue(_ =>
this._stateChangeProcessEnqueue(_ =>
this._changeServerURL(prefs.get("serverURL"), STARTING_SERVICE_EVENT));
}
},
@ -770,16 +764,6 @@ var PushService = {
});
},
/**
* Dispatches a broadcast notification to the BroadcastService.
*/
receivedBroadcastMessage(message) {
pushBroadcastService.receivedBroadcastMessage(message.broadcasts)
.catch(e => {
console.error(e);
});;
},
/**
* Updates a registration record after receiving a push message.
*
@ -1098,21 +1082,6 @@ var PushService = {
});
},
/*
* Called only by the PushBroadcastService on the receipt of a new
* subscription. Don't call this directly. Go through PushBroadcastService.
*/
async subscribeBroadcast(broadcastId, version) {
if (this._state != PUSH_SERVICE_RUNNING) {
// Ignore any request to subscribe before we send a hello.
// We'll send all the broadcast listeners as part of the hello
// anyhow.
return;
}
await this._service.sendSubscribeBroadcast(broadcastId, version);
},
/**
* Called on message from the child process.
*

View File

@ -167,7 +167,7 @@ var PushServiceAndroidGCM = {
// No action required.
},
connect: function(records, broadcastListeners) {
connect: function(records) {
console.debug("connect:", records);
// It's possible for the registration or subscriptions backing the
// PushService to not be registered with the underlying AndroidPushService.
@ -194,10 +194,6 @@ var PushServiceAndroidGCM = {
});
},
sendSubscribeBroadcast: async function(serviceId, version) {
// Not implemented yet
},
isConnected: function() {
return this._mainPushService != null;
},

View File

@ -426,14 +426,10 @@ var PushServiceHttp2 = {
return serverURI.scheme == "https";
},
connect: function(subscriptions, broadcastListeners) {
connect: function(subscriptions) {
this.startConnections(subscriptions);
},
sendSubscribeBroadcast: async function(serviceId, version) {
// Not implemented yet
},
isConnected: function() {
return this._mainPushService != null;
},

View File

@ -314,8 +314,6 @@ var PushServiceWebSocket = {
this._mainPushService = mainPushService;
this._serverURI = serverURI;
// Filled in at connect() time
this._broadcastListeners = null;
// Override the default WebSocket factory function. The returned object
// must be null or satisfy the nsIWebSocketChannel interface. Used by
@ -514,9 +512,8 @@ var PushServiceWebSocket = {
}
},
connect: function(records, broadcastListeners) {
console.debug("connect()", broadcastListeners);
this._broadcastListeners = broadcastListeners;
connect: function(records) {
console.debug("connect()");
this._beginWSSetup();
},
@ -569,13 +566,6 @@ var PushServiceWebSocket = {
this._currentState = STATE_READY;
prefs.observe("userAgentID", this);
// Handle broadcasts received in response to the "hello" message.
if (reply.broadcasts) {
// The reply isn't technically a broadcast message, but it has
// the shape of a broadcast message (it has a broadcasts field).
this._mainPushService.receivedBroadcastMessage(reply);
}
this._dataEnabled = !!reply.use_webpush;
if (this._dataEnabled) {
this._mainPushService.getAllUnexpired().then(records =>
@ -757,10 +747,6 @@ var PushServiceWebSocket = {
}
},
_handleBroadcastReply: function(reply) {
this._mainPushService.receivedBroadcastMessage(reply);
},
reportDeliveryError(messageID, reason) {
console.debug("reportDeliveryError()");
let code = kDELIVERY_REASON_TO_CODE[reason];
@ -959,7 +945,6 @@ var PushServiceWebSocket = {
let data = {
messageType: "hello",
broadcasts: this._broadcastListeners,
use_webpush: true,
};
@ -1028,7 +1013,7 @@ var PushServiceWebSocket = {
// A whitelist of protocol handlers. Add to these if new messages are added
// in the protocol.
let handlers = ["Hello", "Register", "Unregister", "Notification", "Broadcast"];
let handlers = ["Hello", "Register", "Unregister", "Notification"];
// Build up the handler name to call from messageType.
// e.g. messageType == "register" -> _handleRegisterReply.
@ -1127,17 +1112,6 @@ var PushServiceWebSocket = {
}
return request;
},
sendSubscribeBroadcast(serviceId, version) {
let data = {
messageType: "broadcast_subscribe",
broadcasts: {
[serviceId]: version
},
};
this._queueRequest(data);
},
};
function PushRecordWebSocket(record) {

View File

@ -13,7 +13,6 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
'PushBroadcastService.jsm',
'PushCrypto.jsm',
'PushDB.jsm',
'PushRecord.jsm',

View File

@ -1,16 +0,0 @@
"use strict";
var EXPORTED_SYMBOLS = ["broadcastHandler"];
var broadcastHandler = {
reset() {
this.notifications = [];
this.wasNotified = new Promise((resolve, reject) => {
this.receivedBroadcastMessage = function() {
resolve();
this.notifications.push(Array.from(arguments));
};
});
}
};

View File

@ -15,9 +15,7 @@ ChromeUtils.defineModuleGetter(this, 'PlacesTestUtils',
XPCOMUtils.defineLazyServiceGetter(this, 'PushServiceComponent',
'@mozilla.org/push/Service;1', 'nsIPushService');
const pushServiceExports = ChromeUtils.import('resource://gre/modules/PushService.jsm', {});
const broadcastServiceExports = ChromeUtils.import('resource://gre/modules/PushBroadcastService.jsm', {});
const serviceExports = {...pushServiceExports, ...broadcastServiceExports};
const serviceExports = ChromeUtils.import('resource://gre/modules/PushService.jsm', {});
const servicePrefs = new Preferences('dom.push.');
const WEBSOCKET_CLOSE_GOING_AWAY = 1001;
@ -187,7 +185,6 @@ function MockWebSocket(originalURI, handlers = {}) {
this._onUnregister = handlers.onUnregister;
this._onACK = handlers.onACK;
this._onPing = handlers.onPing;
this._onBroadcastSubscribe = handlers.onBroadcastSubscribe;
}
MockWebSocket.prototype = {
@ -260,13 +257,6 @@ MockWebSocket.prototype = {
}
break;
case 'broadcast_subscribe':
if (typeof this._onBroadcastSubscribe != 'function') {
throw new Error('Unexpected broadcast_subscribe');
}
this._onBroadcastSubscribe(request);
break;
default:
throw new Error('Unexpected message: ' + messageType);
}

View File

@ -2,7 +2,3 @@ EXTRA_COMPONENTS += [
'PushServiceHandler.js',
'PushServiceHandler.manifest',
]
EXTRA_JS_MODULES += [
'broadcast_handler.jsm',
]

View File

@ -1,265 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
const {PushDB, PushService, PushServiceWebSocket, pushBroadcastService: broadcastService} = serviceExports;
const {BroadcastService} = ChromeUtils.import("resource://gre/modules/PushBroadcastService.jsm", {});
ChromeUtils.import("resource://gre/modules/JSONFile.jsm");
ChromeUtils.import("resource://testing-common/Assert.jsm");
ChromeUtils.import("resource://testing-common/FileTestUtils.jsm");
ChromeUtils.import("resource://test/broadcast_handler.jsm");
const assert = new Assert();
const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
const channelID = '0ef2ad4a-6c49-41ad-af6e-95d2425276bf';
function run_test() {
do_get_profile();
setPrefs({
userAgentID,
requestTimeout: 1000,
retryBaseInterval: 150
});
run_next_test();
}
function getPushServiceMock() {
return {
subscribed: [],
subscribeBroadcast: function(broadcastId, version) {
this.subscribed.push([broadcastId, version]);
},
};
}
add_task(async function test_register_success() {
await broadcastService._resetListeners();
let db = PushServiceWebSocket.newPushDB();
broadcastHandler.reset();
let notifications = broadcastHandler.notifications;
let socket;
registerCleanupFunction(() => {return db.drop().then(_ => db.close());});
await broadcastService.addListener("broadcast-test", "2018-02-01", {
moduleURI: "resource://test/broadcast_handler.jsm",
symbolName: "broadcastHandler",
});
PushServiceWebSocket._generateID = () => channelID;
var broadcastSubscriptions = [];
await PushService.init({
serverURI: "wss://push.example.org/",
db,
makeWebSocket(uri) {
return new MockWebSocket(uri, {
onHello(data) {
socket = this;
deepEqual(data.broadcasts, {"broadcast-test": "2018-02-01"}, "Handshake: doesn't consult listeners");
equal(data.messageType, 'hello', 'Handshake: wrong message type');
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
this.serverSendMsg(JSON.stringify({
messageType: 'hello',
status: 200,
uaid: userAgentID
}));
},
onBroadcastSubscribe(data) {
broadcastSubscriptions.push(data);
},
});
}
})
socket.serverSendMsg(JSON.stringify({
messageType: "broadcast",
broadcasts: {
"broadcast-test": "2018-03-02"
}
}));
await broadcastHandler.wasNotified;
deepEqual(notifications, [["2018-03-02", "broadcast-test"]], "Broadcast notification didn't get delivered");
deepEqual(await broadcastService.getListeners(), {
"broadcast-test": "2018-03-02"
}, "Broadcast version wasn't updated");
await broadcastService.addListener("example-listener", "2018-03-01", {
moduleURI: "resource://gre/modules/not-real-example.jsm",
symbolName: "doesntExist"
});
deepEqual(broadcastSubscriptions, [{
messageType: "broadcast_subscribe",
broadcasts: {"example-listener": "2018-03-01"}
}]);
});
add_task(async function test_handle_hello_broadcasts() {
PushService.uninit();
await broadcastService._resetListeners();
let db = PushServiceWebSocket.newPushDB();
broadcastHandler.reset();
let notifications = broadcastHandler.notifications;
registerCleanupFunction(() => {return db.drop().then(_ => db.close());});
await broadcastService.addListener("broadcast-test", "2018-02-01", {
moduleURI: "resource://test/broadcast_handler.jsm",
symbolName: "broadcastHandler",
});
PushServiceWebSocket._generateID = () => channelID;
await PushService.init({
serverURI: "wss://push.example.org/",
db,
makeWebSocket(uri) {
return new MockWebSocket(uri, {
onHello(data) {
deepEqual(data.broadcasts, {"broadcast-test": "2018-02-01"}, "Handshake: doesn't consult listeners");
equal(data.messageType, 'hello', 'Handshake: wrong message type');
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
this.serverSendMsg(JSON.stringify({
messageType: 'hello',
status: 200,
uaid: userAgentID,
broadcasts: {
"broadcast-test": "2018-02-02"
}
}));
},
onBroadcastSubscribe(data) {
broadcastSubscriptions.push(data);
},
});
}
})
await broadcastHandler.wasNotified;
deepEqual(notifications, [["2018-02-02", "broadcast-test"]], "Broadcast notification on hello was delivered");
deepEqual(await broadcastService.getListeners(), {
"broadcast-test": "2018-02-02"
}, "Broadcast version wasn't updated");
});
add_task(async function test_broadcast_unit() {
const fakeListenersData = {
"abc": {
version: "2018-03-04",
sourceInfo: {
moduleURI: "resource://gre/modules/abc.jsm",
symbolName: "getAbc"
}
},
"def": {
version: "2018-04-05",
sourceInfo: {
moduleURI: "resource://gre/modules/def.jsm",
symbolName: "getDef"
}
}
};
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const jsonFile = new JSONFile({path});
jsonFile.data = {
listeners: fakeListenersData,
};
await jsonFile._save();
const pushServiceMock = getPushServiceMock();
const broadcastService = new BroadcastService(pushServiceMock, path);
const listeners = await broadcastService.getListeners();
deepEqual(listeners, {
"abc": "2018-03-04",
"def": "2018-04-05"
});
await broadcastService.addListener("ghi", "2018-05-06", {
moduleURI: "resource://gre/modules/ghi.jsm",
symbolName: "getGhi"
});
deepEqual(pushServiceMock.subscribed, [
["ghi", "2018-05-06"]
]);
await broadcastService._saveImmediately();
const newJSONFile = new JSONFile({path});
await newJSONFile.load();
deepEqual(newJSONFile.data, {
listeners: {
...fakeListenersData,
ghi: {
version: "2018-05-06",
sourceInfo: {
moduleURI: "resource://gre/modules/ghi.jsm",
symbolName: "getGhi"
}
}
},
version: 1,
});
deepEqual(await broadcastService.getListeners(), {
"abc": "2018-03-04",
"def": "2018-04-05",
"ghi": "2018-05-06"
});
});
add_task(async function test_broadcast_initialize_sane() {
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const broadcastService = new BroadcastService(getPushServiceMock(), path);
deepEqual(await broadcastService.getListeners(), {}, "listeners should start out sane");
await broadcastService._saveImmediately();
let onDiskJSONFile = new JSONFile({path});
await onDiskJSONFile.load();
deepEqual(onDiskJSONFile.data, {listeners: {}, version: 1},
"written JSON file has listeners and version fields");
await broadcastService.addListener("ghi", "2018-05-06", {
moduleURI: "resource://gre/modules/ghi.jsm",
symbolName: "getGhi"
});
await broadcastService._saveImmediately();
onDiskJSONFile = new JSONFile({path});
await onDiskJSONFile.load();
deepEqual(onDiskJSONFile.data, {
listeners: {
ghi: {
version: "2018-05-06",
sourceInfo: {
moduleURI: "resource://gre/modules/ghi.jsm",
symbolName: "getGhi"
}
}
},
version: 1,
}, "adding listeners to initial state is written OK");
});
add_task(async function test_broadcast_reject_invalid_sourceinfo() {
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const broadcastService = new BroadcastService(getPushServiceMock(), path);
await assert.rejects(broadcastService.addListener("ghi", "2018-05-06", {
moduleName: "resource://gre/modules/ghi.jsm",
symbolName: "getGhi"
}), "missing moduleURI", "rejects sourceInfo that doesn't have moduleURI");
});

View File

@ -2,9 +2,7 @@
head = head.js head-http2.js
# Push notifications and alarms are currently disabled on Android.
skip-if = toolkit == 'android'
support-files = broadcast_handler.jsm
[test_broadcast_success.js]
[test_clear_forgetAboutSite.js]
[test_clear_origin_data.js]
[test_crypto.js]

View File

@ -2,14 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global __URI__ */
"use strict";
var EXPORTED_SYMBOLS = [
"RemoteSettings",
"jexlFilterFunc",
"remoteSettingsBroadcastHandler",
"jexlFilterFunc"
];
ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -29,8 +26,6 @@ ChromeUtils.defineModuleGetter(this, "ClientEnvironmentBase",
"resource://gre/modules/components-utils/ClientEnvironment.jsm");
ChromeUtils.defineModuleGetter(this, "FilterExpressions",
"resource://gre/modules/components-utils/FilterExpressions.jsm");
ChromeUtils.defineModuleGetter(this, "pushBroadcastService",
"resource://gre/modules/PushBroadcastService.jsm");
const PREF_SETTINGS_SERVER = "services.settings.server";
const PREF_SETTINGS_DEFAULT_BUCKET = "services.settings.default_bucket";
@ -738,23 +733,7 @@ function remoteSettingsFunction() {
Services.obs.notifyObservers(null, "remote-settings-changes-polled");
};
const broadcastID = "remote-settings/monitor_changes";
const currentVersion = Services.prefs.getStringValue(PREF_SETTINGS_LAST_ETAG, "");
const moduleInfo = {
moduleURI: __URI__,
symbolName: "remoteSettingsBroadcastHandler",
};
pushBroadcastService.addListener(broadcastID, currentVersion,
moduleInfo);
return remoteSettings;
}
var RemoteSettings = remoteSettingsFunction();
var remoteSettingsBroadcastHandler = {
async receivedBroadcastMessage(data, broadcastID) {
return RemoteSettings.pollChanges();
}
};