diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /browser/extensions/report-site-issue/experimentalAPIs | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
11 files changed, 760 insertions, 0 deletions
diff --git a/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.js b/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.js new file mode 100644 index 0000000000..0e8b6346c4 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.js @@ -0,0 +1,39 @@ +/* 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"; + +/* global ExtensionAPI, ExtensionCommon, Services */ + +this.aboutConfigPrefs = class extends ExtensionAPI { + getAPI(context) { + const EventManager = ExtensionCommon.EventManager; + const extensionIDBase = context.extension.id.split("@")[0]; + const endpointPrefName = `extensions.${extensionIDBase}.newIssueEndpoint`; + + return { + aboutConfigPrefs: { + onEndpointPrefChange: new EventManager({ + context, + name: "aboutConfigPrefs.onEndpointPrefChange", + register: fire => { + const callback = () => { + fire.async().catch(() => {}); // ignore Message Manager disconnects + }; + Services.prefs.addObserver(endpointPrefName, callback); + return () => { + Services.prefs.removeObserver(endpointPrefName, callback); + }; + }, + }).api(), + async getEndpointPref() { + return Services.prefs.getStringPref(endpointPrefName, undefined); + }, + async setEndpointPref(value) { + Services.prefs.setStringPref(endpointPrefName, value); + }, + }, + }; + } +}; diff --git a/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.json b/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.json new file mode 100644 index 0000000000..1fd313e392 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.json @@ -0,0 +1,35 @@ +[ + { + "namespace": "aboutConfigPrefs", + "description": "experimental API extension to allow access to about:config preferences", + "events": [ + { + "name": "onEndpointPrefChange", + "type": "function", + "parameters": [] + } + ], + "functions": [ + { + "name": "getEndpointPref", + "type": "function", + "description": "Get the endpoint preference's value", + "parameters": [], + "async": true + }, + { + "name": "setEndpointPref", + "type": "function", + "description": "Set the endpoint preference's value", + "parameters": [ + { + "name": "value", + "type": "string", + "description": "The new value" + } + ], + "async": true + } + ] + } +] diff --git a/browser/extensions/report-site-issue/experimentalAPIs/actors/tabExtrasActor.jsm b/browser/extensions/report-site-issue/experimentalAPIs/actors/tabExtrasActor.jsm new file mode 100644 index 0000000000..52bc6c56c6 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/actors/tabExtrasActor.jsm @@ -0,0 +1,163 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["ReportSiteIssueHelperChild"]; + +const PREVIEW_MAX_ITEMS = 10; +const LOG_LEVELS = ["debug", "info", "warn", "error"]; + +function getPreview(value) { + switch (typeof value) { + case "symbol": + return value.toString(); + + case "function": + return "function ()"; + + case "object": + if (value === null) { + return null; + } + + if (Array.isArray(value)) { + return `(${value.length})[...]`; + } + + return "{...}"; + + case "undefined": + return "undefined"; + + default: + try { + structuredClone(value); + } catch (_) { + return `${value}` || "?"; + } + + return value; + } +} + +function getArrayPreview(arr) { + const preview = []; + let count = 0; + for (const value of arr) { + if (++count > PREVIEW_MAX_ITEMS) { + break; + } + preview.push(getPreview(value)); + } + + return preview; +} + +function getObjectPreview(obj) { + const preview = {}; + let count = 0; + for (const key of Object.keys(obj)) { + if (++count > PREVIEW_MAX_ITEMS) { + break; + } + preview[key] = getPreview(obj[key]); + } + + return preview; +} + +function getArgs(value) { + if (typeof value === "object" && value !== null) { + if (Array.isArray(value)) { + return getArrayPreview(value); + } + + return getObjectPreview(value); + } + + return getPreview(value); +} + +class ReportSiteIssueHelperChild extends JSWindowActorChild { + _getConsoleMessages(windowId) { + const ConsoleAPIStorage = Cc[ + "@mozilla.org/consoleAPI-storage;1" + ].getService(Ci.nsIConsoleAPIStorage); + let messages = ConsoleAPIStorage.getEvents(windowId); + return messages.map(evt => { + const { columnNumber, filename, level, lineNumber, timeStamp } = evt; + const args = evt.arguments.map(getArgs); + + const message = { + level, + log: args, + uri: filename, + pos: `${lineNumber}:${columnNumber}`, + }; + + return { timeStamp, message }; + }); + } + + _getScriptErrors(windowId, includePrivate) { + const messages = Services.console.getMessageArray(); + return messages + .filter(message => { + if (message instanceof Ci.nsIScriptError) { + if (!includePrivate && message.isFromPrivateWindow) { + return false; + } + + if (windowId && windowId !== message.innerWindowID) { + return false; + } + + return true; + } + + // If this is not an nsIScriptError and we need to do window-based + // filtering we skip this message. + return false; + }) + .map(error => { + const { + timeStamp, + errorMessage, + sourceName, + lineNumber, + columnNumber, + logLevel, + } = error; + const message = { + level: LOG_LEVELS[logLevel], + log: [errorMessage], + uri: sourceName, + pos: `${lineNumber}:${columnNumber}`, + }; + return { timeStamp, message }; + }); + } + + _getLoggedMessages(includePrivate = false) { + const windowId = this.contentWindow.windowGlobalChild.innerWindowId; + return this._getConsoleMessages(windowId).concat( + this._getScriptErrors(windowId, includePrivate) + ); + } + + receiveMessage(msg) { + switch (msg.name) { + case "GetLog": + return this._getLoggedMessages(); + case "GetBlockingStatus": + const { docShell } = this; + return { + hasTrackingContentBlocked: docShell.hasTrackingContentBlocked, + }; + } + return null; + } +} diff --git a/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.js b/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.js new file mode 100644 index 0000000000..ce4466f1cf --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.js @@ -0,0 +1,197 @@ +/* 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"; + +/* global AppConstants, ExtensionAPI, Services */ + +function isTelemetryEnabled() { + return Services.prefs.getBoolPref( + "datareporting.healthreport.uploadEnabled", + false + ); +} + +function getSysinfoProperty(propertyName, defaultValue) { + try { + return Services.sysinfo.getProperty(propertyName); + } catch (e) {} + + return defaultValue; +} + +function getUserAgent() { + const { userAgent } = Cc[ + "@mozilla.org/network/protocol;1?name=http" + ].getService(Ci.nsIHttpProtocolHandler); + return userAgent; +} + +function getGfxData() { + const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); + const data = {}; + + try { + const { + compositor, + hwCompositing, + openglCompositing, + wrCompositor, + wrSoftware, + } = gfxInfo.getFeatures(); + + data.features = { + compositor, + hwCompositing, + openglCompositing, + wrCompositor, + wrSoftware, + }; + } catch (e) {} + + try { + if (AppConstants.platform !== "android") { + data.monitors = gfxInfo.getMonitors(); + } + } catch (e) {} + + return data; +} + +function limitStringToLength(str, maxLength) { + if (typeof str !== "string") { + return null; + } + return str.substring(0, maxLength); +} + +function getSecurityAppData() { + const maxStringLength = 256; + + const keys = [ + ["registeredAntiVirus", "antivirus"], + ["registeredAntiSpyware", "antispyware"], + ["registeredFirewall", "firewall"], + ]; + + let result = {}; + + for (let [inKey, outKey] of keys) { + let prop = getSysinfoProperty(inKey, null); + if (prop) { + prop = limitStringToLength(prop, maxStringLength).split(";"); + } + + result[outKey] = prop; + } + + return result; +} + +function getAdditionalPrefs() { + const prefs = {}; + for (const [name, dflt] of Object.entries({ + "browser.opaqueResponseBlocking": false, + "extensions.InstallTrigger.enabled": false, + "gfx.canvas.accelerated.force-enabled": false, + "gfx.webrender.compositor.force-enabled": false, + "privacy.resistFingerprinting": false, + })) { + prefs[name] = Services.prefs.getBoolPref(name, dflt); + } + const cookieBehavior = "network.cookie.cookieBehavior"; + prefs[cookieBehavior] = Services.prefs.getIntPref(cookieBehavior); + + return prefs; +} + +function getMemoryMB() { + let memoryMB = getSysinfoProperty("memsize", null); + if (memoryMB) { + memoryMB = Math.round(memoryMB / 1024 / 1024); + } + + return memoryMB; +} + +this.browserInfo = class extends ExtensionAPI { + getAPI(context) { + return { + browserInfo: { + async getGraphicsPrefs() { + const prefs = {}; + for (const [name, dflt] of Object.entries({ + "layers.acceleration.force-enabled": false, + "gfx.webrender.all": false, + "gfx.webrender.blob-images": true, + "gfx.webrender.enabled": false, + "image.mem.shared": true, + })) { + prefs[name] = Services.prefs.getBoolPref(name, dflt); + } + return prefs; + }, + async getAppVersion() { + return AppConstants.MOZ_APP_VERSION; + }, + async getBlockList() { + const trackingTable = Services.prefs.getCharPref( + "urlclassifier.trackingTable" + ); + // If content-track-digest256 is in the tracking table, + // the user has enabled the strict list. + return trackingTable.includes("content") ? "strict" : "basic"; + }, + async getBuildID() { + return Services.appinfo.appBuildID; + }, + async getUpdateChannel() { + return AppConstants.MOZ_UPDATE_CHANNEL; + }, + async getPlatform() { + return AppConstants.platform; + }, + async hasTouchScreen() { + const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService( + Ci.nsIGfxInfo + ); + return gfxInfo.getInfo().ApzTouchInput == 1; + }, + async getAdditionalData() { + const blockList = await this.getBlockList(); + const userAgent = getUserAgent(); + const gfxData = getGfxData(); + const prefs = getAdditionalPrefs(); + const memoryMb = getMemoryMB(); + + const data = { + applicationName: Services.appinfo.name, + version: Services.appinfo.version, + updateChannel: AppConstants.MOZ_UPDATE_CHANNEL, + osArchitecture: getSysinfoProperty("arch", null), + osName: getSysinfoProperty("name", null), + osVersion: getSysinfoProperty("version", null), + fissionEnabled: Services.appinfo.fissionAutostart, + userAgent, + gfxData, + blockList, + prefs, + memoryMb, + }; + + if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) { + data.sec = getSecurityAppData(); + } + + if (AppConstants.platform === "android") { + data.device = getSysinfoProperty("device", null); + data.isTablet = getSysinfoProperty("tablet", false); + } + + return data; + }, + }, + }; + } +}; diff --git a/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.json b/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.json new file mode 100644 index 0000000000..c12f2ceb2e --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/browserInfo.json @@ -0,0 +1,64 @@ +[ + { + "namespace": "browserInfo", + "description": "experimental API extensions to get browser info not exposed via web APIs", + "functions": [ + { + "name": "getAppVersion", + "type": "function", + "description": "Gets the app version", + "parameters": [], + "async": true + }, + { + "name": "getBlockList", + "type": "function", + "description": "Gets the current blocklist", + "parameters": [], + "async": true + }, + { + "name": "getBuildID", + "type": "function", + "description": "Gets the build ID", + "parameters": [], + "async": true + }, + { + "name": "getGraphicsPrefs", + "type": "function", + "description": "Gets interesting about:config prefs for graphics", + "parameters": [], + "async": true + }, + { + "name": "getPlatform", + "type": "function", + "description": "Gets the platform", + "parameters": [], + "async": true + }, + { + "name": "getUpdateChannel", + "type": "function", + "description": "Gets the update channel", + "parameters": [], + "async": true + }, + { + "name": "hasTouchScreen", + "type": "function", + "description": "Gets whether a touchscreen is present", + "parameters": [], + "async": true + }, + { + "name": "getAdditionalData", + "type": "function", + "description": "Gets additional info for the new reporter experiment", + "parameters": [], + "async": true + } + ] + } +] diff --git a/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.js b/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.js new file mode 100644 index 0000000000..804f4b08d5 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.js @@ -0,0 +1,38 @@ +/* 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"; + +/* global ExtensionAPI, ExtensionCommon, Services */ + +const TOPIC = "report-site-issue"; + +this.helpMenu = class extends ExtensionAPI { + getAPI(context) { + const { tabManager } = context.extension; + let EventManager = ExtensionCommon.EventManager; + + return { + helpMenu: { + onHelpMenuCommand: new EventManager({ + context, + name: "helpMenu", + register: fire => { + let observer = (subject, topic, data) => { + let nativeTab = subject.wrappedJSObject; + let tab = tabManager.convert(nativeTab); + fire.async(tab); + }; + + Services.obs.addObserver(observer, TOPIC); + + return () => { + Services.obs.removeObserver(observer, TOPIC); + }; + }, + }).api(), + }, + }; + } +}; diff --git a/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.json b/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.json new file mode 100644 index 0000000000..e7c3a8c405 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/helpMenu.json @@ -0,0 +1,28 @@ +[ + { + "namespace": "helpMenu", + "events": [ + { + "name": "onHelpMenuCommand", + "type": "function", + "async": "callback", + "description": "Fired when the command event for the Report Site Issue menuitem in Help is fired.", + "parameters": [ + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tab", + "$ref": "tabs.Tab", + "optional": true, + "description": "Details about the selected tab in the window where the menuitem command fired." + } + ] + } + ] + } + ] + } +] diff --git a/browser/extensions/report-site-issue/experimentalAPIs/l10n.js b/browser/extensions/report-site-issue/experimentalAPIs/l10n.js new file mode 100644 index 0000000000..1c91e7a04d --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/l10n.js @@ -0,0 +1,55 @@ +/* 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"; + +/* global ExtensionAPI, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyGetter(this, "l10nStrings", function () { + return Services.strings.createBundle( + "chrome://report-site-issue/locale/webcompat.properties" + ); +}); + +let l10nManifest; + +this.l10n = class extends ExtensionAPI { + onShutdown(isAppShutdown) { + if (!isAppShutdown && l10nManifest) { + Components.manager.removeBootstrappedManifestLocation(l10nManifest); + } + } + getAPI(context) { + // Until we move to Fluent (bug 1446164), we're stuck with + // chrome.manifest for handling localization since its what the + // build system can handle for localized repacks. + if (context.extension.rootURI instanceof Ci.nsIJARURI) { + l10nManifest = context.extension.rootURI.JARFile.QueryInterface( + Ci.nsIFileURL + ).file; + } else if (context.extension.rootURI instanceof Ci.nsIFileURL) { + l10nManifest = context.extension.rootURI.file; + } + + if (l10nManifest) { + Components.manager.addBootstrappedManifestLocation(l10nManifest); + } else { + console.error( + "Cannot find webcompat reporter chrome.manifest for registering translated strings" + ); + } + + return { + l10n: { + getMessage(name) { + try { + return Promise.resolve(l10nStrings.GetStringFromName(name)); + } catch (e) { + return Promise.reject(e); + } + }, + }, + }; + } +}; diff --git a/browser/extensions/report-site-issue/experimentalAPIs/l10n.json b/browser/extensions/report-site-issue/experimentalAPIs/l10n.json new file mode 100644 index 0000000000..60942e726c --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/l10n.json @@ -0,0 +1,21 @@ +[ + { + "namespace": "l10n", + "description": "A stop-gap L10N API only meant to be used until a Fluent-based API is added in bug 1425104", + "functions": [ + { + "name": "getMessage", + "type": "function", + "description": "Gets the message with the given name", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The name of the message" + } + ], + "async": true + } + ] + } +] diff --git a/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.js b/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.js new file mode 100644 index 0000000000..12ccd91e05 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.js @@ -0,0 +1,99 @@ +/* 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"; + +/* global ExtensionAPI, XPCOMUtils, Services */ + +XPCOMUtils.defineLazyServiceGetter( + this, + "resProto", + "@mozilla.org/network/protocol;1?name=resource", + "nsISubstitutingProtocolHandler" +); + +this.tabExtras = class extends ExtensionAPI { + constructor(extension) { + super(extension); + this._registerActorModule(); + } + + getAPI(context) { + const { tabManager } = context.extension; + return { + tabExtras: { + async getWebcompatInfo(tabId) { + const { + browser: { browsingContext }, + incognito, + } = tabManager.get(tabId); + const actors = gatherActors("ReportSiteIssueHelper", browsingContext); + const promises = actors.map(actor => actor.sendQuery("GetLog")); + const logs = await Promise.all(promises); + const info = await actors[0].sendQuery("GetBlockingStatus"); + info.hasMixedActiveContentBlocked = !!( + browsingContext.secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT + ); + info.hasMixedDisplayContentBlocked = !!( + browsingContext.secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT + ); + info.isPB = incognito; + info.log = logs + .flat() + .sort((a, b) => a.timeStamp - b.timeStamp) + .map(m => m.message); + return info; + }, + }, + }; + } + + onShutdown(isAppShutdown) { + this._unregisterActorModule(); + } + + _registerActorModule() { + resProto.setSubstitution( + "report-site-issue", + Services.io.newURI( + "experimentalAPIs/actors/", + null, + this.extension.rootURI + ) + ); + ChromeUtils.registerWindowActor("ReportSiteIssueHelper", { + child: { + moduleURI: "resource://report-site-issue/tabExtrasActor.jsm", + }, + allFrames: true, + }); + } + + _unregisterActorModule() { + ChromeUtils.unregisterWindowActor("ReportSiteIssueHelper"); + resProto.setSubstitution("report-site-issue", null); + } +}; + +function getActorForBrowsingContext(name, browsingContext) { + const windowGlobal = browsingContext.currentWindowGlobal; + return windowGlobal ? windowGlobal.getActor(name) : null; +} + +function gatherActors(name, browsingContext) { + const list = []; + + const actor = getActorForBrowsingContext(name, browsingContext); + if (actor) { + list.push(actor); + } + + for (const child of browsingContext.children) { + list.push(...gatherActors(name, child)); + } + + return list; +} diff --git a/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.json b/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.json new file mode 100644 index 0000000000..7769049798 --- /dev/null +++ b/browser/extensions/report-site-issue/experimentalAPIs/tabExtras.json @@ -0,0 +1,21 @@ +[ + { + "namespace": "tabExtras", + "description": "experimental tab API extensions", + "functions": [ + { + "name": "getWebcompatInfo", + "type": "function", + "description": "Gets the content blocking status and script log for a given tab", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + } + ], + "async": true + } + ] + } +] |