forked from mirrors/gecko-dev
Bug 1882422: JSActor and Component Structure to get some data back from JS-land in C++-land r=Gijs
- Create a new XPCOM component UserCharacteristicsPageService backed by a JS implementation. It will be accessed as a Service, so there is only ever one of them - Inside UserCharacteristicsPageService use a HiddenFrame to open a page invisible to the user. (In this patch it's a random https:// site, in the next patch it will become an about: page) - Create a Parent/Child JSWindowActor so that the hidden webpage (which will eventually be in the privilegedabout content process) can communicate back to the parent. - Use Topics/Observers so the Actor can be registered when we need it and then unregistered when we're done. The registering occurs in a RFPHelper which a separate thing already running and listening for stuff. - Inside nsUserCharacteristics.cpp::CanvasStuff() we ask our service to load the webpage, then will await a promise indicating the page has been loaded. That promise is the return value of UserCharacteristicsPageService::createContentPage() and is resolved after UserCharacteristicsPageService::pageLoaded calls backgroundResolve(). That, in turn, happens after UserCharacteristicsParent gets the message from UserCharacteristicsChild that the DOMContentLoaded event has occurred. The HiddenFrame stuff is influenced by PageDataService.sys.mjs - that's where the HiddenBrowserManager stuff came from. In the next patch we'll actually do something when that promise is resolved. Differential Revision: https://phabricator.services.mozilla.com/D202893
This commit is contained in:
parent
2d6b956c61
commit
c8e3989334
10 changed files with 350 additions and 7 deletions
|
|
@ -641,6 +641,7 @@ pref("toolkit.telemetry.user_characteristics_ping.last_version_sent", 0);
|
|||
// the telemetry client id (which is not sent in this ping), it is cleared when a
|
||||
// user opts-out of telemetry, it is set upon first telemetry submission
|
||||
pref("toolkit.telemetry.user_characteristics_ping.uuid", "");
|
||||
pref("toolkit.telemetry.user_characteristics_ping.logLevel", "Warn");
|
||||
|
||||
// AsyncShutdown delay before crashing in case of shutdown freeze
|
||||
// ASan, TSan and code coverage builds can be considerably slower. Extend the
|
||||
|
|
|
|||
29
toolkit/actors/UserCharacteristicsChild.sys.mjs
Normal file
29
toolkit/actors/UserCharacteristicsChild.sys.mjs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "console", () => {
|
||||
return console.createInstance({
|
||||
prefix: "UserCharacteristicsPage",
|
||||
maxLogLevelPref: "toolkit.telemetry.user_characteristics_ping.logLevel",
|
||||
});
|
||||
});
|
||||
|
||||
export class UserCharacteristicsChild extends JSWindowActorChild {
|
||||
handleEvent(event) {
|
||||
lazy.console.debug("Got ", event.type);
|
||||
switch (event.type) {
|
||||
case "DOMContentLoaded":
|
||||
case "pageshow":
|
||||
lazy.console.debug("creating IdleDispatch");
|
||||
ChromeUtils.idleDispatch(() => {
|
||||
lazy.console.debug("sending PageReady");
|
||||
this.sendAsyncMessage("UserCharacteristics::PageReady");
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
toolkit/actors/UserCharacteristicsParent.sys.mjs
Normal file
32
toolkit/actors/UserCharacteristicsParent.sys.mjs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
//import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
ChromeUtils.defineLazyGetter(lazy, "console", () => {
|
||||
return console.createInstance({
|
||||
prefix: "UserCharacteristicsPage",
|
||||
maxLogLevelPref: "toolkit.telemetry.user_characteristics_ping.logLevel",
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
lazy,
|
||||
"UserCharacteristicsPageService",
|
||||
"@mozilla.org/user-characteristics-page;1",
|
||||
"nsIUserCharacteristicsPageService"
|
||||
);
|
||||
*/
|
||||
|
||||
export class UserCharacteristicsParent extends JSWindowActorParent {
|
||||
receiveMessage(aMessage) {
|
||||
lazy.console.debug("Got ", aMessage.name);
|
||||
if (aMessage.name == "UserCharacteristics::PageReady") {
|
||||
lazy.console.debug("Got pageReady");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,6 +75,8 @@ FINAL_TARGET_FILES.actors += [
|
|||
"UAWidgetsChild.sys.mjs",
|
||||
"UnselectedTabHoverChild.sys.mjs",
|
||||
"UnselectedTabHoverParent.sys.mjs",
|
||||
"UserCharacteristicsChild.sys.mjs",
|
||||
"UserCharacteristicsParent.sys.mjs",
|
||||
"ViewSourceChild.sys.mjs",
|
||||
"ViewSourcePageChild.sys.mjs",
|
||||
"ViewSourcePageParent.sys.mjs",
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ class _RFPHelper {
|
|||
this._initialized = true;
|
||||
|
||||
// Add unconditional observers
|
||||
Services.obs.addObserver(this, "user-characteristics-populating-data");
|
||||
Services.obs.addObserver(this, "user-characteristics-populating-data-done");
|
||||
Services.prefs.addObserver(kPrefResistFingerprinting, this);
|
||||
Services.prefs.addObserver(kPrefLetterboxing, this);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
|
@ -80,6 +82,12 @@ class _RFPHelper {
|
|||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "user-characteristics-populating-data":
|
||||
this._registerUserCharacteristicsActor();
|
||||
break;
|
||||
case "user-characteristics-populating-data-done":
|
||||
this._unregisterUserCharacteristicsActor();
|
||||
break;
|
||||
case "nsPref:changed":
|
||||
this._handlePrefChanged(data);
|
||||
break;
|
||||
|
|
@ -97,6 +105,27 @@ class _RFPHelper {
|
|||
}
|
||||
}
|
||||
|
||||
_registerUserCharacteristicsActor() {
|
||||
log("_registerUserCharacteristicsActor()");
|
||||
ChromeUtils.registerWindowActor("UserCharacteristics", {
|
||||
parent: {
|
||||
esModuleURI: "resource://gre/actors/UserCharacteristicsParent.sys.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "resource://gre/actors/UserCharacteristicsChild.sys.mjs",
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
pageshow: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_unregisterUserCharacteristicsActor() {
|
||||
log("_unregisterUserCharacteristicsActor()");
|
||||
ChromeUtils.unregisterWindowActor("UserCharacteristics");
|
||||
}
|
||||
|
||||
handleEvent(aMessage) {
|
||||
switch (aMessage.type) {
|
||||
case "TabOpen": {
|
||||
|
|
@ -283,16 +312,16 @@ class _RFPHelper {
|
|||
_handleLetterboxingPrefChanged() {
|
||||
if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) {
|
||||
Services.ww.registerNotification(this);
|
||||
this._registerActor();
|
||||
this._registerLetterboxingActor();
|
||||
this._attachAllWindows();
|
||||
} else {
|
||||
this._unregisterActor();
|
||||
this._unregisterLetterboxingActor();
|
||||
this._detachAllWindows();
|
||||
Services.ww.unregisterNotification(this);
|
||||
}
|
||||
}
|
||||
|
||||
_registerActor() {
|
||||
_registerLetterboxingActor() {
|
||||
ChromeUtils.registerWindowActor("RFPHelper", {
|
||||
parent: {
|
||||
esModuleURI: "resource:///actors/RFPHelperParent.sys.mjs",
|
||||
|
|
@ -307,7 +336,7 @@ class _RFPHelper {
|
|||
});
|
||||
}
|
||||
|
||||
_unregisterActor() {
|
||||
_unregisterLetterboxingActor() {
|
||||
ChromeUtils.unregisterWindowActor("RFPHelper");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
/* 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/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
HiddenFrame: "resource://gre/modules/HiddenFrame.sys.mjs",
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "console", () => {
|
||||
return console.createInstance({
|
||||
prefix: "UserCharacteristicsPage",
|
||||
maxLogLevelPref: "toolkit.telemetry.user_characteristics_ping.logLevel",
|
||||
});
|
||||
});
|
||||
|
||||
const BACKGROUND_WIDTH = 1024;
|
||||
const BACKGROUND_HEIGHT = 768;
|
||||
|
||||
/**
|
||||
* A manager for hidden browsers. Responsible for creating and destroying a
|
||||
* hidden frame to hold them.
|
||||
* All of this is copied from PageDataService.sys.mjs
|
||||
*/
|
||||
class HiddenBrowserManager {
|
||||
/**
|
||||
* The hidden frame if one has been created.
|
||||
*
|
||||
* @type {HiddenFrame | null}
|
||||
*/
|
||||
#frame = null;
|
||||
/**
|
||||
* The number of hidden browser elements currently in use.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
#browsers = 0;
|
||||
|
||||
/**
|
||||
* Creates and returns a new hidden browser.
|
||||
*
|
||||
* @returns {Browser}
|
||||
*/
|
||||
async #acquireBrowser() {
|
||||
this.#browsers++;
|
||||
if (!this.#frame) {
|
||||
this.#frame = new lazy.HiddenFrame();
|
||||
}
|
||||
|
||||
let frame = await this.#frame.get();
|
||||
let doc = frame.document;
|
||||
let browser = doc.createXULElement("browser");
|
||||
browser.setAttribute("remote", "true");
|
||||
browser.setAttribute("type", "content");
|
||||
browser.setAttribute(
|
||||
"style",
|
||||
`
|
||||
width: ${BACKGROUND_WIDTH}px;
|
||||
min-width: ${BACKGROUND_WIDTH}px;
|
||||
height: ${BACKGROUND_HEIGHT}px;
|
||||
min-height: ${BACKGROUND_HEIGHT}px;
|
||||
`
|
||||
);
|
||||
browser.setAttribute("maychangeremoteness", "true");
|
||||
doc.documentElement.appendChild(browser);
|
||||
|
||||
return browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the given hidden browser.
|
||||
*
|
||||
* @param {Browser} browser
|
||||
* The hidden browser element.
|
||||
*/
|
||||
#releaseBrowser(browser) {
|
||||
browser.remove();
|
||||
|
||||
this.#browsers--;
|
||||
if (this.#browsers == 0) {
|
||||
this.#frame.destroy();
|
||||
this.#frame = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a callback function with a new hidden browser.
|
||||
* This function will return whatever the callback function returns.
|
||||
*
|
||||
* @param {Callback} callback
|
||||
* The callback function will be called with the browser element and may
|
||||
* be asynchronous.
|
||||
* @returns {T}
|
||||
*/
|
||||
async withHiddenBrowser(callback) {
|
||||
let browser = await this.#acquireBrowser();
|
||||
try {
|
||||
return await callback(browser);
|
||||
} finally {
|
||||
this.#releaseBrowser(browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UserCharacteristicsPageService {
|
||||
classId = Components.ID("{ce3e9659-e311-49fb-b18b-7f27c6659b23}");
|
||||
QueryInterface = ChromeUtils.generateQI([
|
||||
"nsIUserCharacteristicsPageService",
|
||||
]);
|
||||
|
||||
_initialized = false;
|
||||
_isParentProcess = false;
|
||||
|
||||
/**
|
||||
* A manager for hidden browsers.
|
||||
*
|
||||
* @type {HiddenBrowserManager}
|
||||
*/
|
||||
_browserManager = new HiddenBrowserManager();
|
||||
|
||||
/**
|
||||
* A map of hidden browsers to a resolve function that should be passed the
|
||||
* actor that was created for the browser.
|
||||
*
|
||||
* @type {WeakMap<Browser, function(PageDataParent): void>}
|
||||
*/
|
||||
_backgroundBrowsers = new WeakMap();
|
||||
|
||||
constructor() {
|
||||
lazy.console.debug("Init");
|
||||
|
||||
if (
|
||||
Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT
|
||||
) {
|
||||
throw new Error(
|
||||
"Shouldn't init UserCharacteristicsPage in content processes."
|
||||
);
|
||||
}
|
||||
|
||||
// Return if we have initiated.
|
||||
if (this._initialized) {
|
||||
lazy.console.warn("preventing re-initilization...");
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
lazy.console.debug("Init completes");
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
lazy.console.debug("shutdown");
|
||||
}
|
||||
|
||||
createContentPage() {
|
||||
lazy.console.debug("called createContentPage");
|
||||
return this._browserManager.withHiddenBrowser(async browser => {
|
||||
lazy.console.debug(`In withHiddenBrowser`);
|
||||
try {
|
||||
let { promise, resolve } = Promise.withResolvers();
|
||||
this._backgroundBrowsers.set(browser, resolve);
|
||||
|
||||
let userCharacteristicsPageURI =
|
||||
Services.io.newURI("https://ritter.vg");
|
||||
let principal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
let loadURIOptions = {
|
||||
triggeringPrincipal: principal,
|
||||
};
|
||||
browser.loadURI(userCharacteristicsPageURI, loadURIOptions);
|
||||
|
||||
await promise;
|
||||
|
||||
lazy.console.debug("Unregistering actor");
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"user-characteristics-populating-data-done"
|
||||
);
|
||||
|
||||
lazy.console.debug(`Returning`);
|
||||
} finally {
|
||||
lazy.console.debug(`In finally`);
|
||||
this._backgroundBrowsers.delete(browser);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async pageLoaded(browsingContext) {
|
||||
lazy.console.debug(`pageLoaded browsingContext=${browsingContext}`);
|
||||
|
||||
let browser = browsingContext.embedderElement;
|
||||
|
||||
let backgroundResolve = this._backgroundBrowsers.get(browser);
|
||||
if (backgroundResolve) {
|
||||
backgroundResolve();
|
||||
return;
|
||||
}
|
||||
throw new Error(`No backround resolve for ${browser} found`);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
Classes = [
|
||||
{
|
||||
|
|
@ -34,4 +34,11 @@ Classes = [
|
|||
'constructor': 'FingerprintingWebCompatService',
|
||||
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
|
||||
},
|
||||
{
|
||||
'cid': '{ce3e9659-e311-49fb-b18b-7f27c6659b23}',
|
||||
'contract_ids': ['@mozilla.org/user-characteristics-page;1'],
|
||||
'esModule': 'resource://gre/modules/UserCharacteristicsPageService.sys.mjs',
|
||||
'constructor': 'UserCharacteristicsPageService',
|
||||
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Privacy: Anti-Tracking")
|
||||
|
|
@ -39,6 +39,7 @@ EXPORTS.mozilla.gtest += ["nsUserCharacteristics.h"]
|
|||
EXTRA_JS_MODULES += [
|
||||
"FingerprintingWebCompatService.sys.mjs",
|
||||
"RFPHelper.sys.mjs",
|
||||
"UserCharacteristicsPageService.sys.mjs",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "toolkit_resistfingerprinting"
|
||||
|
|
@ -50,6 +51,7 @@ XPCOM_MANIFESTS += [
|
|||
XPIDL_SOURCES += [
|
||||
"nsIFingerprintingWebCompatService.idl",
|
||||
"nsIRFPService.idl",
|
||||
"nsIUserCharacteristicsPageService.idl",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
webidl BrowsingContext;
|
||||
|
||||
[scriptable, uuid(ce3e9659-e311-49fb-b18b-7f27c6659b23)]
|
||||
interface nsIUserCharacteristicsPageService : nsISupports {
|
||||
|
||||
/* (In the next patch, this will:)
|
||||
* Create the UserCharacteristics about: page as a HiddenFrame
|
||||
* and begin the data collection.
|
||||
*/
|
||||
Promise createContentPage();
|
||||
|
||||
/*
|
||||
* Called when the UserCharacteristics about: page has been loaded
|
||||
*/
|
||||
void pageLoaded(in BrowsingContext browsingContext);
|
||||
};
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsUserCharacteristics.h"
|
||||
|
||||
#include "nsID.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsIUserCharacteristicsPageService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/glean/GleanPings.h"
|
||||
#include "mozilla/glean/GleanMetrics.h"
|
||||
|
||||
|
|
@ -59,6 +62,15 @@ int MaxTouchPoints() {
|
|||
}; // namespace testing
|
||||
|
||||
// ==================================================================
|
||||
void CanvasStuff() {
|
||||
nsCOMPtr<nsIUserCharacteristicsPageService> ucp =
|
||||
do_GetService("@mozilla.org/user-characteristics-page;1");
|
||||
MOZ_ASSERT(ucp);
|
||||
|
||||
RefPtr<mozilla::dom::Promise> promise;
|
||||
mozilla::Unused << ucp->CreateContentPage(getter_AddRefs(promise));
|
||||
}
|
||||
|
||||
void PopulateCSSProperties() {
|
||||
glean::characteristics::prefers_reduced_transparency.Set(
|
||||
LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency));
|
||||
|
|
@ -242,6 +254,13 @@ const auto* const kUUIDPref =
|
|||
nsresult nsUserCharacteristics::PopulateData(bool aTesting /* = false */) {
|
||||
MOZ_LOG(gUserCharacteristicsLog, LogLevel::Warning, ("Populating Data"));
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
obs->NotifyObservers(nullptr, "user-characteristics-populating-data",
|
||||
nullptr);
|
||||
|
||||
glean::characteristics::submission_schema.Set(kSubmissionSchema);
|
||||
|
||||
nsAutoCString uuidString;
|
||||
|
|
|
|||
Loading…
Reference in a new issue