1406 строки
41 KiB
JavaScript
1406 строки
41 KiB
JavaScript
/* -*- Mode: JavaScript; 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/. */
|
|
|
|
"use strict";
|
|
|
|
var EXPORTED_SYMBOLS = ["MailGlue", "MailTelemetryForTests"];
|
|
|
|
const { XPCOMUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
|
);
|
|
|
|
const { AppConstants } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AppConstants.sys.mjs"
|
|
);
|
|
|
|
const lazy = {};
|
|
|
|
// lazy module getter
|
|
|
|
XPCOMUtils.defineLazyGetter(lazy, "gMailBundle", function () {
|
|
return Services.strings.createBundle(
|
|
"chrome://messenger/locale/messenger.properties"
|
|
);
|
|
});
|
|
|
|
if (AppConstants.NIGHTLY_BUILD) {
|
|
XPCOMUtils.defineLazyGetter(
|
|
lazy,
|
|
"WeaveService",
|
|
() => Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
|
|
);
|
|
}
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
ActorManagerParent: "resource://gre/modules/ActorManagerParent.sys.mjs",
|
|
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
|
|
ChatCore: "resource:///modules/chatHandler.sys.mjs",
|
|
ExtensionSupport: "resource:///modules/ExtensionSupport.sys.mjs",
|
|
|
|
LightweightThemeConsumer:
|
|
"resource://gre/modules/LightweightThemeConsumer.sys.mjs",
|
|
|
|
OsEnvironment: "resource://gre/modules/OsEnvironment.sys.mjs",
|
|
PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
|
|
|
|
RemoteSecuritySettings:
|
|
"resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs",
|
|
|
|
cal: "resource:///modules/calendar/calUtils.sys.mjs",
|
|
});
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(lazy, {
|
|
MailMigrator: "resource:///modules/MailMigrator.jsm",
|
|
MailServices: "resource:///modules/MailServices.jsm",
|
|
MailUsageTelemetry: "resource:///modules/MailUsageTelemetry.jsm",
|
|
OAuth2Providers: "resource:///modules/OAuth2Providers.jsm",
|
|
TBDistCustomizer: "resource:///modules/TBDistCustomizer.jsm",
|
|
});
|
|
|
|
if (AppConstants.MOZ_UPDATER) {
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
UpdateListener: "resource://gre/modules/UpdateListener.sys.mjs",
|
|
});
|
|
}
|
|
|
|
const listeners = {
|
|
observers: {},
|
|
|
|
observe(subject, topic, data) {
|
|
for (const module of this.observers[topic]) {
|
|
try {
|
|
lazy[module].observe(subject, topic, data);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
},
|
|
|
|
init() {
|
|
for (const observer of Object.keys(this.observers)) {
|
|
Services.obs.addObserver(this, observer);
|
|
}
|
|
},
|
|
};
|
|
if (AppConstants.MOZ_UPDATER) {
|
|
listeners.observers["update-downloading"] = ["UpdateListener"];
|
|
listeners.observers["update-staged"] = ["UpdateListener"];
|
|
listeners.observers["update-downloaded"] = ["UpdateListener"];
|
|
listeners.observers["update-available"] = ["UpdateListener"];
|
|
listeners.observers["update-error"] = ["UpdateListener"];
|
|
listeners.observers["update-swap"] = ["UpdateListener"];
|
|
}
|
|
|
|
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
|
|
|
|
const JSWINDOWACTORS = {
|
|
ChatAction: {
|
|
matches: ["chrome://chat/content/conv.html"],
|
|
parent: {
|
|
esModuleURI: "resource:///actors/ChatActionParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/ChatActionChild.sys.mjs",
|
|
events: {
|
|
contextmenu: { mozSystemGroup: true },
|
|
},
|
|
},
|
|
},
|
|
|
|
ContextMenu: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/ContextMenuParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/ContextMenuChild.sys.mjs",
|
|
events: {
|
|
contextmenu: { mozSystemGroup: true },
|
|
},
|
|
},
|
|
allFrames: true,
|
|
},
|
|
|
|
// As in ActorManagerParent.sys.mjs, but with single-site and single-page
|
|
// message manager groups added.
|
|
FindBar: {
|
|
parent: {
|
|
esModuleURI: "resource://gre/actors/FindBarParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource://gre/actors/FindBarChild.sys.mjs",
|
|
events: {
|
|
keypress: { mozSystemGroup: true },
|
|
},
|
|
},
|
|
|
|
allFrames: true,
|
|
messageManagerGroups: [
|
|
"browsers",
|
|
"single-site",
|
|
"single-page",
|
|
"test",
|
|
"",
|
|
],
|
|
},
|
|
|
|
LinkClickHandler: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/LinkClickHandlerParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/LinkClickHandlerChild.sys.mjs",
|
|
events: {
|
|
click: {},
|
|
},
|
|
},
|
|
messageManagerGroups: ["single-site", "webext-browsers"],
|
|
allFrames: true,
|
|
},
|
|
|
|
LinkHandler: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/LinkHandlerParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/LinkHandlerChild.sys.mjs",
|
|
events: {
|
|
DOMHeadElementParsed: {},
|
|
DOMLinkAdded: {},
|
|
DOMLinkChanged: {},
|
|
pageshow: {},
|
|
// The `pagehide` event is only used to clean up state which will not be
|
|
// present if the actor hasn't been created.
|
|
pagehide: { createActor: false },
|
|
},
|
|
},
|
|
|
|
messageManagerGroups: ["browsers", "single-site", "single-page"],
|
|
},
|
|
|
|
// As in ActorManagerParent.sys.mjs, but with single-site and single-page
|
|
// message manager groups added.
|
|
LoginManager: {
|
|
parent: {
|
|
esModuleURI: "resource://gre/modules/LoginManagerParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource://gre/modules/LoginManagerChild.sys.mjs",
|
|
events: {
|
|
DOMDocFetchSuccess: {},
|
|
DOMFormBeforeSubmit: {},
|
|
DOMFormHasPassword: {},
|
|
DOMInputPasswordAdded: {},
|
|
},
|
|
},
|
|
|
|
allFrames: true,
|
|
messageManagerGroups: [
|
|
"browsers",
|
|
"single-site",
|
|
"single-page",
|
|
"webext-browsers",
|
|
"",
|
|
],
|
|
},
|
|
|
|
MailLink: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/MailLinkParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/MailLinkChild.sys.mjs",
|
|
events: {
|
|
click: {},
|
|
},
|
|
},
|
|
allFrames: true,
|
|
},
|
|
|
|
Pdfjs: {
|
|
parent: {
|
|
esModuleURI: "resource://pdf.js/PdfjsParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource://pdf.js/PdfjsChild.sys.mjs",
|
|
},
|
|
enablePreference: PREF_PDFJS_ISDEFAULT_CACHE_STATE,
|
|
allFrames: true,
|
|
},
|
|
|
|
Prompt: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/PromptParent.sys.mjs",
|
|
},
|
|
includeChrome: true,
|
|
allFrames: true,
|
|
},
|
|
|
|
StrictLinkClickHandler: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/LinkClickHandlerParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/LinkClickHandlerChild.sys.mjs",
|
|
events: {
|
|
click: {},
|
|
},
|
|
},
|
|
messageManagerGroups: ["single-page"],
|
|
allFrames: true,
|
|
},
|
|
|
|
VCard: {
|
|
parent: {
|
|
esModuleURI: "resource:///actors/VCardParent.sys.mjs",
|
|
},
|
|
child: {
|
|
esModuleURI: "resource:///actors/VCardChild.sys.mjs",
|
|
events: {
|
|
click: {},
|
|
},
|
|
},
|
|
allFrames: true,
|
|
},
|
|
};
|
|
|
|
// Seconds of idle time before the late idle tasks will be scheduled.
|
|
const LATE_TASKS_IDLE_TIME_SEC = 20;
|
|
|
|
// Time after we stop tracking startup crashes.
|
|
const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;
|
|
|
|
/**
|
|
* Glue code that should be executed before any windows are opened. Any
|
|
* window-independent helper methods (a la nsBrowserGlue.js) should go in
|
|
* MailUtils.jsm instead.
|
|
*/
|
|
|
|
function MailGlue() {
|
|
XPCOMUtils.defineLazyServiceGetter(
|
|
this,
|
|
"_userIdleService",
|
|
"@mozilla.org/widget/useridleservice;1",
|
|
"nsIUserIdleService"
|
|
);
|
|
this._init();
|
|
}
|
|
|
|
// This should match the constant of the same name in devtools
|
|
// (devtools/client/framework/browser-toolbox/Launcher.sys.mjs). Otherwise the logic
|
|
// in command-line-startup will fail. We have a test to ensure it matches, at
|
|
// mail/base/test/unit/test_devtools_url.js.
|
|
MailGlue.BROWSER_TOOLBOX_WINDOW_URL =
|
|
"chrome://devtools/content/framework/browser-toolbox/window.html";
|
|
|
|
// A Promise that is resolved by an idle task after most start-up operations.
|
|
MailGlue.afterStartUp = new Promise(resolve => {
|
|
MailGlue.resolveAfterStartUp = resolve;
|
|
});
|
|
|
|
MailGlue.prototype = {
|
|
_isNewProfile: undefined,
|
|
|
|
// init (called at app startup)
|
|
_init() {
|
|
// Start-up notifications, in order.
|
|
// app-startup happens first, registered in components.conf.
|
|
Services.obs.addObserver(this, "command-line-startup");
|
|
Services.obs.addObserver(this, "final-ui-startup");
|
|
Services.obs.addObserver(this, "quit-application-granted");
|
|
Services.obs.addObserver(this, "mail-startup-done");
|
|
|
|
// Shut-down notifications.
|
|
Services.obs.addObserver(this, "xpcom-shutdown");
|
|
|
|
// General notifications.
|
|
Services.obs.addObserver(this, "intl:app-locales-changed");
|
|
Services.obs.addObserver(this, "handle-xul-text-link");
|
|
Services.obs.addObserver(this, "chrome-document-global-created");
|
|
Services.obs.addObserver(this, "document-element-inserted");
|
|
Services.obs.addObserver(this, "handlersvc-store-initialized");
|
|
|
|
// Call the lazy getter to ensure ActorManagerParent is initialized.
|
|
lazy.ActorManagerParent;
|
|
|
|
// FindBar and LoginManager actors are included in JSWINDOWACTORS as they
|
|
// also apply to the single-site and single-page message manager groups.
|
|
// First we must unregister them to avoid errors.
|
|
ChromeUtils.unregisterWindowActor("FindBar");
|
|
ChromeUtils.unregisterWindowActor("LoginManager");
|
|
|
|
lazy.ActorManagerParent.addJSWindowActors(JSWINDOWACTORS);
|
|
},
|
|
|
|
// cleanup (called at shutdown)
|
|
_dispose() {
|
|
Services.obs.removeObserver(this, "command-line-startup");
|
|
Services.obs.removeObserver(this, "final-ui-startup");
|
|
Services.obs.removeObserver(this, "quit-application-granted");
|
|
// mail-startup-done is removed by its handler.
|
|
|
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
|
|
|
Services.obs.removeObserver(this, "intl:app-locales-changed");
|
|
Services.obs.removeObserver(this, "handle-xul-text-link");
|
|
Services.obs.removeObserver(this, "chrome-document-global-created");
|
|
Services.obs.removeObserver(this, "document-element-inserted");
|
|
Services.obs.removeObserver(this, "handlersvc-store-initialized");
|
|
|
|
lazy.ExtensionSupport.unregisterWindowListener(
|
|
"Thunderbird-internal-BrowserConsole"
|
|
);
|
|
|
|
lazy.MailUsageTelemetry.uninit();
|
|
|
|
if (this._lateTasksIdleObserver) {
|
|
this._userIdleService.removeIdleObserver(
|
|
this._lateTasksIdleObserver,
|
|
LATE_TASKS_IDLE_TIME_SEC
|
|
);
|
|
delete this._lateTasksIdleObserver;
|
|
}
|
|
},
|
|
|
|
// nsIObserver implementation
|
|
observe(aSubject, aTopic, aData) {
|
|
switch (aTopic) {
|
|
case "app-startup":
|
|
// Record the previously started version. This is used to check for
|
|
// extensions that were disabled by an application update. We need to
|
|
// read this pref before the Add-Ons Manager changes it.
|
|
this.previousVersion = Services.prefs.getCharPref(
|
|
"extensions.lastAppVersion",
|
|
"0"
|
|
);
|
|
break;
|
|
case "command-line-startup":
|
|
// Check if this process is the developer toolbox process, and if it
|
|
// is, stop MailGlue from doing anything more. Also sets a flag that
|
|
// can be checked to see if this is the toolbox process.
|
|
let isToolboxProcess = false;
|
|
const commandLine = aSubject.QueryInterface(Ci.nsICommandLine);
|
|
const flagIndex = commandLine.findFlag("chrome", true) + 1;
|
|
if (
|
|
flagIndex > 0 &&
|
|
flagIndex < commandLine.length &&
|
|
commandLine.getArgument(flagIndex) ===
|
|
MailGlue.BROWSER_TOOLBOX_WINDOW_URL
|
|
) {
|
|
isToolboxProcess = true;
|
|
}
|
|
|
|
MailGlue.__defineGetter__("isToolboxProcess", () => isToolboxProcess);
|
|
|
|
if (isToolboxProcess) {
|
|
// Clean up all of the listeners.
|
|
this._dispose();
|
|
}
|
|
break;
|
|
case "final-ui-startup":
|
|
// Initialise the permission manager. If this happens before telling
|
|
// the folder service that strings are available, it's a *much* less
|
|
// expensive operation than if it happens afterwards, because if
|
|
// strings are available, some types of mail URL go looking for things
|
|
// in message databases, causing massive amounts of I/O.
|
|
Services.perms.all;
|
|
|
|
Cc["@mozilla.org/msgFolder/msgFolderService;1"]
|
|
.getService(Ci.nsIMsgFolderService)
|
|
.initializeFolderStrings();
|
|
Cc["@mozilla.org/msgDBView/msgDBViewService;1"]
|
|
.getService(Ci.nsIMsgDBViewService)
|
|
.initializeDBViewStrings();
|
|
this._beforeUIStartup();
|
|
break;
|
|
case "quit-application-granted":
|
|
Services.startup.trackStartupCrashEnd();
|
|
if (AppConstants.MOZ_UPDATER) {
|
|
lazy.UpdateListener.reset();
|
|
}
|
|
break;
|
|
case "mail-startup-done":
|
|
this._onFirstWindowLoaded();
|
|
Services.obs.removeObserver(this, "mail-startup-done");
|
|
break;
|
|
case "xpcom-shutdown":
|
|
this._dispose();
|
|
break;
|
|
case "intl:app-locales-changed":
|
|
Cc["@mozilla.org/msgFolder/msgFolderService;1"]
|
|
.getService(Ci.nsIMsgFolderService)
|
|
.initializeFolderStrings();
|
|
Cc["@mozilla.org/msgDBView/msgDBViewService;1"]
|
|
.getService(Ci.nsIMsgDBViewService)
|
|
.initializeDBViewStrings();
|
|
const windows = Services.wm.getEnumerator("mail:3pane");
|
|
while (windows.hasMoreElements()) {
|
|
const tabmail = windows.getNext().document.getElementById("tabmail");
|
|
if (tabmail) {
|
|
for (const tab of tabmail.tabInfo) {
|
|
if (tab.mode.name == "mail3PaneTab") {
|
|
tab.chromeBrowser?.contentWindow.threadTree?.invalidate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Refresh the folder tree.
|
|
const fls = Cc["@mozilla.org/mail/folder-lookup;1"].getService(
|
|
Ci.nsIFolderLookupService
|
|
);
|
|
fls.setPrettyNameFromOriginalAllFolders();
|
|
break;
|
|
case "handle-xul-text-link":
|
|
this._handleLink(aSubject, aData);
|
|
break;
|
|
case "chrome-document-global-created":
|
|
// Set up lwt, but only if the "lightweightthemes" attr is set on the root
|
|
// (i.e. in messenger.xhtml).
|
|
aSubject.addEventListener(
|
|
"DOMContentLoaded",
|
|
() => {
|
|
if (
|
|
aSubject.document.documentElement.hasAttribute(
|
|
"lightweightthemes"
|
|
)
|
|
) {
|
|
new lazy.LightweightThemeConsumer(aSubject.document);
|
|
}
|
|
},
|
|
{ once: true }
|
|
);
|
|
break;
|
|
case "document-element-inserted":
|
|
const doc = aSubject;
|
|
if (
|
|
doc.nodePrincipal.isSystemPrincipal &&
|
|
(doc.contentType == "application/xhtml+xml" ||
|
|
doc.contentType == "text/html") &&
|
|
// People shouldn't be using our built-in custom elements in
|
|
// system-principal about:blank anyway, and trying to support that
|
|
// causes responsiveness regressions. So let's not support it.
|
|
doc.URL != "about:blank"
|
|
) {
|
|
Services.scriptloader.loadSubScript(
|
|
"chrome://messenger/content/customElements.js",
|
|
doc.ownerGlobal
|
|
);
|
|
}
|
|
break;
|
|
case "handlersvc-store-initialized": {
|
|
// Initialize PdfJs when running in-process and remote. This only
|
|
// happens once since PdfJs registers global hooks. If the PdfJs
|
|
// extension is installed the init method below will be overridden
|
|
// leaving initialization to the extension.
|
|
// parent only: configure default prefs, set up pref observers, register
|
|
// pdf content handler, and initializes parent side message manager
|
|
// shim for privileged api access.
|
|
lazy.PdfJs.init(this._isNewProfile);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
// Runs on startup, before the first command line handler is invoked
|
|
// (i.e. before the first window is opened).
|
|
_beforeUIStartup() {
|
|
lazy.TBDistCustomizer.applyPrefDefaults();
|
|
|
|
const UI_VERSION_PREF = "mail.ui-rdf.version";
|
|
this._isNewProfile = !Services.prefs.prefHasUserValue(UI_VERSION_PREF);
|
|
|
|
// handle any migration work that has to happen at profile startup
|
|
lazy.MailMigrator.migrateAtProfileStartup();
|
|
|
|
if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) {
|
|
lazy.PdfJs.checkIsDefault(this._isNewProfile);
|
|
}
|
|
|
|
// Inject scripts into some devtools windows.
|
|
function _setupBrowserConsole(domWindow) {
|
|
// Browser Console is an XHTML document.
|
|
domWindow.document.title =
|
|
lazy.gMailBundle.GetStringFromName("errorConsoleTitle");
|
|
Services.scriptloader.loadSubScript(
|
|
"chrome://global/content/viewSourceUtils.js",
|
|
domWindow
|
|
);
|
|
}
|
|
|
|
lazy.ExtensionSupport.registerWindowListener(
|
|
"Thunderbird-internal-BrowserConsole",
|
|
{
|
|
chromeURLs: ["chrome://devtools/content/webconsole/index.html"],
|
|
onLoadWindow: _setupBrowserConsole,
|
|
}
|
|
);
|
|
|
|
// check if we're in safe mode
|
|
if (Services.appinfo.inSafeMode) {
|
|
Services.ww.openWindow(
|
|
null,
|
|
"chrome://messenger/content/troubleshootMode.xhtml",
|
|
"_blank",
|
|
"chrome,centerscreen,modal,resizable=no",
|
|
null
|
|
);
|
|
}
|
|
|
|
lazy.AddonManager.maybeInstallBuiltinAddon(
|
|
"thunderbird-compact-light@mozilla.org",
|
|
"1.2",
|
|
"resource://builtin-themes/light/"
|
|
);
|
|
lazy.AddonManager.maybeInstallBuiltinAddon(
|
|
"thunderbird-compact-dark@mozilla.org",
|
|
"1.2",
|
|
"resource://builtin-themes/dark/"
|
|
);
|
|
|
|
if (AppConstants.MOZ_UPDATER) {
|
|
listeners.init();
|
|
}
|
|
},
|
|
|
|
_checkForOldBuildUpdates() {
|
|
// check for update if our build is old
|
|
if (
|
|
AppConstants.MOZ_UPDATER &&
|
|
Services.prefs.getBoolPref("app.update.checkInstallTime")
|
|
) {
|
|
const buildID = Services.appinfo.appBuildID;
|
|
const today = new Date().getTime();
|
|
/* eslint-disable no-multi-spaces */
|
|
const buildDate = new Date(
|
|
buildID.slice(0, 4), // year
|
|
buildID.slice(4, 6) - 1, // months are zero-based.
|
|
buildID.slice(6, 8), // day
|
|
buildID.slice(8, 10), // hour
|
|
buildID.slice(10, 12), // min
|
|
buildID.slice(12, 14)
|
|
) // ms
|
|
.getTime();
|
|
/* eslint-enable no-multi-spaces */
|
|
|
|
const millisecondsIn24Hours = 86400000;
|
|
const acceptableAge =
|
|
Services.prefs.getIntPref("app.update.checkInstallTime.days") *
|
|
millisecondsIn24Hours;
|
|
|
|
if (buildDate + acceptableAge < today) {
|
|
Cc["@mozilla.org/updates/update-service;1"]
|
|
.getService(Ci.nsIApplicationUpdateService)
|
|
.checkForBackgroundUpdates();
|
|
}
|
|
}
|
|
},
|
|
|
|
_onFirstWindowLoaded() {
|
|
// Start these services.
|
|
this._checkForOldBuildUpdates();
|
|
|
|
// On Windows 7 and above, initialize the jump list module.
|
|
const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
|
|
if (
|
|
WINTASKBAR_CONTRACTID in Cc &&
|
|
Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available
|
|
) {
|
|
const { WinTaskbarJumpList } = ChromeUtils.import(
|
|
"resource:///modules/WindowsJumpLists.jsm"
|
|
);
|
|
WinTaskbarJumpList.startup();
|
|
}
|
|
|
|
const { ExtensionsUI } = ChromeUtils.importESModule(
|
|
"resource:///modules/ExtensionsUI.sys.mjs"
|
|
);
|
|
ExtensionsUI.init();
|
|
|
|
// If the application has been updated, check all installed extensions for
|
|
// updates.
|
|
const currentVersion = Services.appinfo.version;
|
|
if (this.previousVersion != "0" && this.previousVersion != currentVersion) {
|
|
const { AddonManager } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AddonManager.sys.mjs"
|
|
);
|
|
const { XPIDatabase } = ChromeUtils.import(
|
|
"resource://gre/modules/addons/XPIDatabase.jsm"
|
|
);
|
|
const addons = XPIDatabase.getAddons();
|
|
for (const addon of addons) {
|
|
if (addon.permissions() & AddonManager.PERM_CAN_UPGRADE) {
|
|
AddonManager.getAddonByID(addon.id).then(addon => {
|
|
if (!AddonManager.shouldAutoUpdate(addon)) {
|
|
return;
|
|
}
|
|
addon.findUpdates(
|
|
{
|
|
onUpdateFinished() {},
|
|
onUpdateAvailable(addon, install) {
|
|
install.install();
|
|
},
|
|
},
|
|
AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED
|
|
);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AppConstants.ASAN_REPORTER) {
|
|
var { AsanReporter } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AsanReporter.sys.mjs"
|
|
);
|
|
AsanReporter.init();
|
|
}
|
|
|
|
// Check if Sync is configured
|
|
if (
|
|
AppConstants.NIGHTLY_BUILD &&
|
|
Services.prefs.prefHasUserValue("services.sync.username")
|
|
) {
|
|
lazy.WeaveService.init();
|
|
}
|
|
|
|
this._scheduleStartupIdleTasks();
|
|
this._lateTasksIdleObserver = (idleService, topic, data) => {
|
|
if (topic == "idle") {
|
|
idleService.removeIdleObserver(
|
|
this._lateTasksIdleObserver,
|
|
LATE_TASKS_IDLE_TIME_SEC
|
|
);
|
|
delete this._lateTasksIdleObserver;
|
|
this._scheduleBestEffortUserIdleTasks();
|
|
}
|
|
};
|
|
this._userIdleService.addIdleObserver(
|
|
this._lateTasksIdleObserver,
|
|
LATE_TASKS_IDLE_TIME_SEC
|
|
);
|
|
|
|
lazy.MailUsageTelemetry.init();
|
|
},
|
|
|
|
/**
|
|
* Use this function as an entry point to schedule tasks that
|
|
* need to run only once after startup, and can be scheduled
|
|
* by using an idle callback.
|
|
*
|
|
* The functions scheduled here will fire from idle callbacks
|
|
* once every window has finished being restored by session
|
|
* restore, and it's guaranteed that they will run before
|
|
* the equivalent per-window idle tasks
|
|
* (from _schedulePerWindowIdleTasks in browser.js).
|
|
*
|
|
* If you have something that can wait even further than the
|
|
* per-window initialization, and is okay with not being run in some
|
|
* sessions, please schedule them using
|
|
* _scheduleBestEffortUserIdleTasks.
|
|
* Don't be fooled by thinking that the use of the timeout parameter
|
|
* will delay your function: it will just ensure that it potentially
|
|
* happens _earlier_ than expected (when the timeout limit has been reached),
|
|
* but it will not make it happen later (and out of order) compared
|
|
* to the other ones scheduled together.
|
|
*/
|
|
_scheduleStartupIdleTasks() {
|
|
const idleTasks = [
|
|
{
|
|
task() {
|
|
// This module needs to be loaded so it registers to receive
|
|
// FormAutoComplete:GetSelectedIndex messages and respond
|
|
// appropriately, otherwise we get error messages like the one
|
|
// reported in bug 1635422.
|
|
ChromeUtils.importESModule(
|
|
"resource://gre/actors/AutoCompleteParent.sys.mjs"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
task() {
|
|
// Make sure Gloda's up and running.
|
|
ChromeUtils.import("resource:///modules/gloda/GlodaPublic.jsm");
|
|
},
|
|
},
|
|
{
|
|
task() {
|
|
MailGlue.resolveAfterStartUp();
|
|
},
|
|
},
|
|
{
|
|
task() {
|
|
const { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
setTimeout(function () {
|
|
Services.tm.idleDispatchToMainThread(
|
|
Services.startup.trackStartupCrashEnd
|
|
);
|
|
}, STARTUP_CRASHES_END_DELAY_MS);
|
|
},
|
|
},
|
|
{
|
|
condition: AppConstants.NIGHTLY_BUILD,
|
|
task: async () => {
|
|
// Register our sync engines.
|
|
await lazy.WeaveService.whenLoaded();
|
|
const Weave = lazy.WeaveService.Weave;
|
|
|
|
for (const [moduleName, engineName] of [
|
|
["accounts", "AccountsEngine"],
|
|
["addressBooks", "AddressBooksEngine"],
|
|
["calendars", "CalendarsEngine"],
|
|
["identities", "IdentitiesEngine"],
|
|
]) {
|
|
const ns = ChromeUtils.importESModule(
|
|
`resource://services-sync/engines/${moduleName}.sys.mjs`
|
|
);
|
|
await Weave.Service.engineManager.register(ns[engineName]);
|
|
Weave.Service.engineManager
|
|
.get(moduleName.toLowerCase())
|
|
.startTracking();
|
|
}
|
|
|
|
if (lazy.WeaveService.enabled) {
|
|
// Schedule a sync (if enabled).
|
|
Weave.Service.scheduler.autoConnect();
|
|
}
|
|
},
|
|
},
|
|
{
|
|
condition: Services.prefs.getBoolPref("mail.chat.enabled"),
|
|
task() {
|
|
lazy.ChatCore.idleStart();
|
|
ChromeUtils.importESModule("resource:///modules/index_im.sys.mjs");
|
|
},
|
|
},
|
|
{
|
|
condition: AppConstants.MOZ_UPDATER,
|
|
task: () => {
|
|
lazy.UpdateListener.maybeShowUnsupportedNotification();
|
|
},
|
|
},
|
|
// This implements a special pref that allows used to launch the
|
|
// application with an immediately opened Storybook when running mach
|
|
// tb-storybook. This pref only needs to work in the local development
|
|
// environment. The URL is hardcoded as to limit what the pref can be used
|
|
// for.
|
|
{
|
|
condition:
|
|
!AppConstants.MOZILLA_OFFICIAL &&
|
|
Services.prefs.getBoolPref("mail.storybook.openTab", false),
|
|
task: () => {
|
|
const win = Services.wm.getMostRecentWindow("mail:3pane");
|
|
if (!win) {
|
|
return;
|
|
}
|
|
const tabmail = win.document.getElementById("tabmail");
|
|
if (!tabmail) {
|
|
return;
|
|
}
|
|
tabmail.openTab("contentTab", { url: "http://localhost:5703" });
|
|
Services.prefs.clearUserPref("mail.storybook.openTab");
|
|
},
|
|
},
|
|
{
|
|
task() {
|
|
// Use idleDispatch a second time to run this after the per-window
|
|
// idle tasks.
|
|
ChromeUtils.idleDispatch(() => {
|
|
Services.obs.notifyObservers(
|
|
null,
|
|
"mail-startup-idle-tasks-finished"
|
|
);
|
|
});
|
|
},
|
|
},
|
|
// Do NOT add anything after idle tasks finished.
|
|
];
|
|
|
|
for (const task of idleTasks) {
|
|
if ("condition" in task && !task.condition) {
|
|
continue;
|
|
}
|
|
|
|
ChromeUtils.idleDispatch(
|
|
() => {
|
|
if (!Services.startup.shuttingDown) {
|
|
const startTime = Cu.now();
|
|
try {
|
|
task.task();
|
|
} catch (ex) {
|
|
console.error(ex);
|
|
} finally {
|
|
ChromeUtils.addProfilerMarker("startupIdleTask", startTime);
|
|
}
|
|
}
|
|
},
|
|
task.timeout ? { timeout: task.timeout } : undefined
|
|
);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Use this function as an entry point to schedule tasks that we hope
|
|
* to run once per session, at any arbitrary point in time, and which we
|
|
* are okay with sometimes not running at all.
|
|
*
|
|
* This function will be called from an idle observer. Check the value of
|
|
* LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
|
|
* observer.
|
|
*
|
|
* Note: this function may never be called if the user is never idle for the
|
|
* requisite time (LATE_TASKS_IDLE_TIME_SEC). Be certain before adding
|
|
* something here that it's okay that it never be run.
|
|
*/
|
|
_scheduleBestEffortUserIdleTasks() {
|
|
const idleTasks = [
|
|
// Certificates revocation list, etc.
|
|
() => lazy.RemoteSecuritySettings.init(),
|
|
// If we haven't already, ensure the address book manager is ready.
|
|
// This must happen at some point so that CardDAV address books sync.
|
|
() => lazy.MailServices.ab.directories,
|
|
// Telemetry.
|
|
async () => {
|
|
lazy.OsEnvironment.reportAllowedAppSources();
|
|
reportAccountTypes();
|
|
reportAddressBookTypes();
|
|
reportAccountSizes();
|
|
await reportCalendars();
|
|
reportPreferences();
|
|
reportUIConfiguration();
|
|
},
|
|
];
|
|
|
|
for (const task of idleTasks) {
|
|
ChromeUtils.idleDispatch(async () => {
|
|
if (!Services.startup.shuttingDown) {
|
|
const startTime = Cu.now();
|
|
try {
|
|
await task();
|
|
} catch (ex) {
|
|
console.error(ex);
|
|
} finally {
|
|
ChromeUtils.addProfilerMarker("startupLateIdleTask", startTime);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
_handleLink(aSubject, aData) {
|
|
const linkHandled = aSubject.QueryInterface(Ci.nsISupportsPRBool);
|
|
if (!linkHandled.data) {
|
|
const win = Services.wm.getMostRecentWindow("mail:3pane");
|
|
aData = JSON.parse(aData);
|
|
const tabParams = { url: aData.href, linkHandler: null };
|
|
if (win) {
|
|
const tabmail = win.document.getElementById("tabmail");
|
|
if (tabmail) {
|
|
tabmail.openTab("contentTab", tabParams);
|
|
win.focus();
|
|
linkHandled.data = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we didn't have an open 3 pane window, try and open one.
|
|
Services.ww.openWindow(
|
|
null,
|
|
"chrome://messenger/content/messenger.xhtml",
|
|
"_blank",
|
|
"chrome,dialog=no,all",
|
|
{
|
|
type: "contentTab",
|
|
tabParams,
|
|
}
|
|
);
|
|
linkHandled.data = true;
|
|
}
|
|
},
|
|
|
|
// for XPCOM
|
|
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
|
|
};
|
|
|
|
/**
|
|
* Report account types to telemetry. For im accounts, use `im_protocol` as
|
|
* scalar key name.
|
|
*/
|
|
function reportAccountTypes() {
|
|
// Init all count with 0, so that when an account was set up before but
|
|
// removed now, we reset it in telemetry report.
|
|
const report = {
|
|
pop3: 0,
|
|
imap: 0,
|
|
nntp: 0,
|
|
exchange: 0,
|
|
rss: 0,
|
|
im_gtalk: 0,
|
|
im_irc: 0,
|
|
im_jabber: 0,
|
|
im_matrix: 0,
|
|
im_odnoklassniki: 0,
|
|
};
|
|
|
|
const providerReport = {
|
|
google: 0,
|
|
microsoft: 0,
|
|
yahoo_aol: 0,
|
|
other: 0,
|
|
};
|
|
|
|
for (const account of lazy.MailServices.accounts.accounts) {
|
|
const incomingServer = account.incomingServer;
|
|
|
|
let type = incomingServer.type;
|
|
if (type == "none") {
|
|
// Reporting one Local Folders account is not that useful. Skip it.
|
|
continue;
|
|
}
|
|
|
|
if (type === "im") {
|
|
const protocol =
|
|
incomingServer.wrappedJSObject.imAccount.protocol.normalizedName;
|
|
type = `im_${protocol}`;
|
|
}
|
|
|
|
// It's still possible to report other types not explicitly specified due to
|
|
// account types that used to exist, but no longer -- e.g. im_yahoo.
|
|
if (!report[type]) {
|
|
report[type] = 0;
|
|
}
|
|
|
|
report[type]++;
|
|
|
|
// Collect a rough understanding of the frequency of various OAuth
|
|
// providers.
|
|
if (incomingServer.authMethod == Ci.nsMsgAuthMethod.OAuth2) {
|
|
const hostnameDetails = lazy.OAuth2Providers.getHostnameDetails(
|
|
incomingServer.hostName
|
|
);
|
|
|
|
if (!hostnameDetails || hostnameDetails.length == 0) {
|
|
// Not a valid OAuth2 configuration; skip it
|
|
continue;
|
|
}
|
|
|
|
const host = hostnameDetails[0];
|
|
|
|
switch (host) {
|
|
case "accounts.google.com":
|
|
providerReport.google++;
|
|
break;
|
|
case "login.microsoftonline.com":
|
|
providerReport.microsoft++;
|
|
break;
|
|
case "login.yahoo.com":
|
|
case "login.aol.com":
|
|
providerReport.yahoo_aol++;
|
|
break;
|
|
default:
|
|
providerReport.other++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const [type, count] of Object.entries(report)) {
|
|
Services.telemetry.keyedScalarSet("tb.account.count", type, count);
|
|
}
|
|
|
|
for (const [provider, count] of Object.entries(providerReport)) {
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.account.oauth2_provider_count",
|
|
provider,
|
|
count
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report size on disk and messages count of each type of folder to telemetry.
|
|
*/
|
|
function reportAccountSizes() {
|
|
const keys = [
|
|
"Inbox",
|
|
"Drafts",
|
|
"Trash",
|
|
"SentMail",
|
|
"Templates",
|
|
"Junk",
|
|
"Archive",
|
|
"Queue",
|
|
];
|
|
for (const key of keys) {
|
|
Services.telemetry.keyedScalarSet("tb.account.total_messages", key, 0);
|
|
}
|
|
Services.telemetry.keyedScalarSet("tb.account.total_messages", "Other", 0);
|
|
Services.telemetry.keyedScalarSet("tb.account.total_messages", "Total", 0);
|
|
|
|
for (const server of lazy.MailServices.accounts.allServers) {
|
|
if (
|
|
server instanceof Ci.nsIPop3IncomingServer &&
|
|
server.deferredToAccount
|
|
) {
|
|
// Skip deferred accounts
|
|
continue;
|
|
}
|
|
|
|
for (const folder of server.rootFolder.descendants) {
|
|
const key =
|
|
keys.find(x => folder.getFlag(Ci.nsMsgFolderFlags[x])) || "Other";
|
|
const totalMessages = folder.getTotalMessages(false);
|
|
if (totalMessages > 0) {
|
|
Services.telemetry.keyedScalarAdd(
|
|
"tb.account.total_messages",
|
|
key,
|
|
totalMessages
|
|
);
|
|
Services.telemetry.keyedScalarAdd(
|
|
"tb.account.total_messages",
|
|
"Total",
|
|
totalMessages
|
|
);
|
|
}
|
|
const sizeOnDisk = folder.sizeOnDisk;
|
|
if (sizeOnDisk > 0) {
|
|
Services.telemetry.keyedScalarAdd(
|
|
"tb.account.size_on_disk",
|
|
key,
|
|
sizeOnDisk
|
|
);
|
|
Services.telemetry.keyedScalarAdd(
|
|
"tb.account.size_on_disk",
|
|
"Total",
|
|
sizeOnDisk
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report addressbook count and contact count to telemetry, keyed by addressbook
|
|
* type. Type is one of ["jsaddrbook", "jscarddav", "moz-abldapdirectory"], see
|
|
* AddrBookManager.jsm for more details.
|
|
*
|
|
* NOTE: We didn't use `dir.dirType` because it's just an integer, instead we
|
|
* use the scheme of `dir.URI` as the type.
|
|
*/
|
|
function reportAddressBookTypes() {
|
|
const report = {};
|
|
for (const dir of lazy.MailServices.ab.directories) {
|
|
const type = dir.URI.split(":")[0];
|
|
|
|
if (!report[type]) {
|
|
report[type] = { count: 0, contactCount: 0 };
|
|
}
|
|
report[type].count++;
|
|
|
|
try {
|
|
report[type].contactCount += dir.childCardCount;
|
|
} catch (ex) {
|
|
// Directories may throw NS_ERROR_NOT_IMPLEMENTED.
|
|
}
|
|
}
|
|
|
|
for (const [type, { count, contactCount }] of Object.entries(report)) {
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.addressbook.addressbook_count",
|
|
type,
|
|
count
|
|
);
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.addressbook.contact_count",
|
|
type,
|
|
contactCount
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A telemetry probe to report calendar count and read only calendar count.
|
|
*/
|
|
async function reportCalendars() {
|
|
const telemetryReport = {};
|
|
const home = lazy.cal.l10n.getCalString("homeCalendarName");
|
|
|
|
for (const calendar of lazy.cal.manager.getCalendars()) {
|
|
if (calendar.name == home && calendar.type == "storage") {
|
|
// Ignore the "Home" calendar if it is disabled or unused as it's
|
|
// automatically added.
|
|
if (calendar.getProperty("disabled")) {
|
|
continue;
|
|
}
|
|
const items = await calendar.getItemsAsArray(
|
|
Ci.calICalendar.ITEM_FILTER_ALL_ITEMS,
|
|
1,
|
|
null,
|
|
null
|
|
);
|
|
if (!items.length) {
|
|
continue;
|
|
}
|
|
}
|
|
if (!telemetryReport[calendar.type]) {
|
|
telemetryReport[calendar.type] = { count: 0, readOnlyCount: 0 };
|
|
}
|
|
telemetryReport[calendar.type].count++;
|
|
if (calendar.readOnly) {
|
|
telemetryReport[calendar.type].readOnlyCount++;
|
|
}
|
|
}
|
|
|
|
for (const [type, { count, readOnlyCount }] of Object.entries(
|
|
telemetryReport
|
|
)) {
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.calendar.calendar_count",
|
|
type.toLowerCase(),
|
|
count
|
|
);
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.calendar.read_only_calendar_count",
|
|
type.toLowerCase(),
|
|
readOnlyCount
|
|
);
|
|
}
|
|
}
|
|
|
|
function reportPreferences() {
|
|
const booleanPrefs = [
|
|
// General
|
|
"browser.cache.disk.smart_size.enabled",
|
|
"privacy.clearOnShutdown.cache",
|
|
"general.autoScroll",
|
|
"general.smoothScroll",
|
|
"intl.regional_prefs.use_os_locales",
|
|
"layers.acceleration.disabled",
|
|
"mail.biff.play_sound",
|
|
"mail.close_message_window.on_delete",
|
|
"mail.delete_matches_sort_order",
|
|
"mail.display_glyph",
|
|
"mail.mailnews.scroll_to_new_message",
|
|
"mail.prompt_purge_threshhold",
|
|
"mail.purge.ask",
|
|
"mail.showCondensedAddresses",
|
|
"mailnews.database.global.indexer.enabled",
|
|
"mailnews.mark_message_read.auto",
|
|
"mailnews.mark_message_read.delay",
|
|
"mailnews.start_page.enabled",
|
|
"searchintegration.enable",
|
|
|
|
// Fonts
|
|
"mail.fixed_width_messages",
|
|
|
|
// Colors
|
|
"browser.display.use_system_colors",
|
|
|
|
// Read receipts
|
|
"mail.mdn.report.enabled",
|
|
"mail.receipt.request_return_receipt_on",
|
|
|
|
// Connection
|
|
"network.proxy.share_proxy_settings",
|
|
"network.proxy.socks_remote_dns",
|
|
"pref.advanced.proxies.disable_button.reload",
|
|
"signon.autologin.proxy",
|
|
|
|
// Offline
|
|
"offline.autoDetect",
|
|
|
|
// Compose
|
|
"ldap_2.autoComplete.useDirectory",
|
|
"mail.collect_email_address_outgoing",
|
|
"mail.compose.attachment_reminder",
|
|
"mail.compose.autosave",
|
|
"mail.compose.big_attachments.notify",
|
|
"mail.compose.default_to_paragraph",
|
|
"mail.e2ee.auto_enable",
|
|
"mail.e2ee.auto_disable",
|
|
"mail.e2ee.notify_on_auto_disable",
|
|
"mail.enable_autocomplete",
|
|
"mail.forward_add_extension",
|
|
"mail.SpellCheckBeforeSend",
|
|
"mail.spellcheck.inline",
|
|
"mail.warn_on_send_accel_key",
|
|
"msgcompose.default_colors",
|
|
"pref.ldap.disable_button.edit_directories",
|
|
|
|
// Send options
|
|
"mailnews.sendformat.auto_downgrade",
|
|
|
|
// Privacy
|
|
"browser.safebrowsing.enabled",
|
|
"mail.phishing.detection.enabled",
|
|
"mail.spam.logging.enabled",
|
|
"mail.spam.manualMark",
|
|
"mail.spam.markAsReadOnSpam",
|
|
"mailnews.downloadToTempFile",
|
|
"mailnews.message_display.disable_remote_image",
|
|
"network.cookie.blockFutureCookies",
|
|
"places.history.enabled",
|
|
"pref.privacy.disable_button.cookie_exceptions",
|
|
"pref.privacy.disable_button.view_cookies",
|
|
"pref.privacy.disable_button.view_passwords",
|
|
"privacy.donottrackheader.enabled",
|
|
"security.disable_button.openCertManager",
|
|
"security.disable_button.openDeviceManager",
|
|
|
|
// Chat
|
|
"messenger.options.getAttentionOnNewMessages",
|
|
"messenger.status.reportIdle",
|
|
"messenger.status.awayWhenIdle",
|
|
"mail.chat.enabled",
|
|
"mail.chat.play_sound",
|
|
"mail.chat.show_desktop_notifications",
|
|
"purple.conversations.im.send_typing",
|
|
"purple.logging.log_chats",
|
|
"purple.logging.log_ims",
|
|
"purple.logging.log_system",
|
|
|
|
// Calendar views
|
|
"calendar.view.showLocation",
|
|
"calendar.view-minimonth.showWeekNumber",
|
|
"calendar.week.d0sundaysoff",
|
|
"calendar.week.d1mondaysoff",
|
|
"calendar.week.d2tuesdaysoff",
|
|
"calendar.week.d3wednesdaysoff",
|
|
"calendar.week.d4thursdaysoff",
|
|
"calendar.week.d5fridaysoff",
|
|
"calendar.week.d6saturdaysoff",
|
|
|
|
// Calendar general
|
|
"calendar.item.editInTab",
|
|
"calendar.item.promptDelete",
|
|
"calendar.timezone.useSystemTimezone",
|
|
|
|
// Alarms
|
|
"calendar.alarms.playsound",
|
|
"calendar.alarms.show",
|
|
"calendar.alarms.showmissed",
|
|
|
|
// Unlisted
|
|
"mail.operate_on_msgs_in_collapsed_threads",
|
|
];
|
|
|
|
const integerPrefs = [
|
|
// Mail UI
|
|
"mail.pane_config.dynamic",
|
|
"mail.ui.display.dateformat.default",
|
|
"mail.ui.display.dateformat.thisweek",
|
|
"mail.ui.display.dateformat.today",
|
|
];
|
|
|
|
// Platform-specific preferences
|
|
if (AppConstants.platform === "win") {
|
|
booleanPrefs.push("mail.biff.show_tray_icon", "mail.minimizeToTray");
|
|
}
|
|
|
|
if (AppConstants.platform !== "macosx") {
|
|
booleanPrefs.push(
|
|
"mail.biff.show_alert",
|
|
"mail.biff.use_system_alert",
|
|
|
|
// Notifications
|
|
"mail.biff.alert.show_preview",
|
|
"mail.biff.alert.show_sender",
|
|
"mail.biff.alert.show_subject"
|
|
);
|
|
}
|
|
|
|
// Compile-time flag-dependent preferences
|
|
if (AppConstants.HAVE_SHELL_SERVICE) {
|
|
booleanPrefs.push("mail.shell.checkDefaultClient");
|
|
}
|
|
|
|
if (AppConstants.MOZ_WIDGET_GTK) {
|
|
booleanPrefs.push("widget.gtk.overlay-scrollbars.enabled");
|
|
}
|
|
|
|
if (AppConstants.MOZ_MAINTENANCE_SERVICE) {
|
|
booleanPrefs.push("app.update.service.enabled");
|
|
}
|
|
|
|
if (AppConstants.MOZ_DATA_REPORTING) {
|
|
booleanPrefs.push("datareporting.healthreport.uploadEnabled");
|
|
}
|
|
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
booleanPrefs.push("browser.crashReports.unsubmittedCheck.autoSubmit2");
|
|
}
|
|
|
|
// Fetch and report preference values
|
|
for (const prefName of booleanPrefs) {
|
|
const prefValue = Services.prefs.getBoolPref(prefName, false);
|
|
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.preferences.boolean",
|
|
prefName,
|
|
prefValue
|
|
);
|
|
}
|
|
|
|
for (const prefName of integerPrefs) {
|
|
const prefValue = Services.prefs.getIntPref(prefName, 0);
|
|
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.preferences.integer",
|
|
prefName,
|
|
prefValue
|
|
);
|
|
}
|
|
}
|
|
|
|
function reportUIConfiguration() {
|
|
const docURL = "chrome://messenger/content/messenger.xhtml";
|
|
|
|
let folderTreeMode = Services.xulStore.getValue(docURL, "folderTree", "mode");
|
|
if (folderTreeMode) {
|
|
const folderTreeCompact = Services.xulStore.getValue(
|
|
docURL,
|
|
"folderTree",
|
|
"compact"
|
|
);
|
|
if (folderTreeCompact === "true") {
|
|
folderTreeMode += " (compact)";
|
|
}
|
|
Services.telemetry.scalarSet(
|
|
"tb.ui.configuration.folder_tree_modes",
|
|
folderTreeMode
|
|
);
|
|
}
|
|
|
|
let headerLayout = Services.xulStore.getValue(
|
|
docURL,
|
|
"messageHeader",
|
|
"layout"
|
|
);
|
|
if (headerLayout) {
|
|
headerLayout = JSON.parse(headerLayout);
|
|
for (let [key, value] of Object.entries(headerLayout)) {
|
|
if (key == "buttonStyle") {
|
|
value = { default: 0, "only-icons": 1, "only-text": 2 }[value];
|
|
}
|
|
Services.telemetry.keyedScalarSet(
|
|
"tb.ui.configuration.message_header",
|
|
key,
|
|
value
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export these functions so we can test them. This object shouldn't be
|
|
* accessed outside of a test (hence the name).
|
|
*/
|
|
var MailTelemetryForTests = {
|
|
reportAccountTypes,
|
|
reportAccountSizes,
|
|
reportAddressBookTypes,
|
|
reportCalendars,
|
|
reportPreferences,
|
|
reportUIConfiguration,
|
|
};
|