Merge mozilla-central to autoland a=merge

This commit is contained in:
Cristina Coroiu 2020-12-07 11:42:35 +02:00
Родитель fd57a078c3 1843375acb
Коммит 6b41a63046
27 изменённых файлов: 67 добавлений и 1045 удалений

Просмотреть файл

@ -154,20 +154,6 @@
</popupnotificationcontent>
</popupnotification>
<popupnotification id="appMenu-update-other-instance-notification"
popupid="update-other-instance"
data-lazy-l10n-id="appmenu-update-other-instance"
data-l10n-attrs="buttonlabel, buttonaccesskey, secondarybuttonlabel, secondarybuttonaccesskey"
closebuttonhidden="true"
dropmarkerhidden="true"
checkboxhidden="true"
buttonhighlight="true"
hidden="true">
<popupnotificationcontent id="update-other-instance-notification-content" orient="vertical">
<description id="update-other-instance-description" data-lazy-l10n-id="appmenu-update-other-instance-message"></description>
</popupnotificationcontent>
</popupnotification>
<popupnotification id="appMenu-addon-installed-notification"
popupid="addon-installed"
closebuttonhidden="true"

Просмотреть файл

@ -55,61 +55,6 @@ add_task(async function init() {
});
});
/**
* Override our binary path so that the update semaphore doesn't think more
* than one instance of this test is running.
* This is a heavily pared down copy of the function in xpcshellUtilsAUS.js.
*/
function adjustGeneralPaths() {
let dirProvider = {
getFile(aProp, aPersistent) {
// Set the value of persistent to false so when this directory provider is
// unregistered it will revert back to the original provider.
aPersistent.value = false;
// The semaphore only needs XRE_EXECUTABLE_FILE, so that's all we need to
// override, we won't bother handling anything else.
if (aProp == XRE_EXECUTABLE_FILE) {
// The temp directory that the mochitest runner creates is unique per
// test, so its path can serve to provide the unique key that the
// update semaphore requires (it doesn't need for this to be the actual
// path to any real file, it's only used as an opaque string).
let tempPath = gEnv.get("MOZ_PROCESS_LOG");
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(tempPath);
return file;
}
return null;
},
QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
};
let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
try {
ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
} catch (_ex) {
// We only override one property, so we have nothing to do if that fails.
return;
}
ds.registerProvider(dirProvider);
registerCleanupFunction(() => {
ds.unregisterProvider(dirProvider);
// Reset the update semaphore once again so that we know the semaphore we're
// interested in here will be closed properly (normally that happens during
// XPCOM shutdown, but that isn't consistent during tests).
let syncManager = Cc[
"@mozilla.org/updates/update-sync-manager;1"
].getService(Ci.nsIUpdateSyncManager);
syncManager.resetSemaphore();
});
// Now that we've overridden the directory provider, the name of the update
// semaphore needs to be changed to match the overridden path.
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
syncManager.resetSemaphore();
}
/**
* Initializes a mock app update. Adapted from runAboutDialogUpdateTest:
* https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/tests/browser/head.js
@ -126,7 +71,6 @@ async function initUpdate(params) {
],
});
adjustGeneralPaths();
await setupTestUpdater();
let queryString = params.queryString ? params.queryString : "";

Просмотреть файл

@ -32,14 +32,6 @@ appmenu-update-restart =
.secondarybuttonlabel = Not Now
.secondarybuttonaccesskey = N
appmenu-update-restart-message = After a quick restart, { -brand-shorter-name } will restore all your open tabs and windows that are not in Private Browsing mode.
appmenu-update-other-instance =
.label = { -brand-shorter-name } is unable to automatically update to the latest version.
.buttonlabel = Update { -brand-shorter-name } anyway
.buttonaccesskey = U
.secondarybuttonlabel = Not Now
.secondarybuttonaccesskey = N
appmenu-update-other-instance-message = A new { -brand-shorter-name } update is available, but it cant be installed because another copy of { -brand-shorter-name } is running. Close it to continue the update, or choose to update anyway (the other copy may not work correctly until you restart it).
appmenu-addon-private-browsing-installed =
.buttonlabel = Okay, Got It
.buttonaccesskey = O

Просмотреть файл

@ -330,18 +330,7 @@ class AppUpdater {
case Cr.NS_OK:
this.aus.removeDownloadListener(this);
if (this.updateStagingEnabled) {
// It could be that another instance was started during the download,
// and if that happened, then we actually should not advance to the
// STAGING status because the staging process isn't really happening
// until that instance exits (or we time out waiting).
if (this.aus.isOtherInstanceHandlingUpdates) {
this._setStatus(AppUpdater.OTHER_INSTANCE_HANDLING_UPDATES);
} else {
this._setStatus(AppUpdater.STATUS.STAGING);
}
// But we should register the staging observer in either case, because
// if we do time out waiting for the other instance to exit, then
// staging really will start at that point.
this._setStatus(AppUpdater.STATUS.STAGING);
this._awaitStagingComplete();
} else {
this._awaitDownloadComplete();

Просмотреть файл

@ -69,7 +69,6 @@
#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-downloading"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-other-instance"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-unsupported"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
border-radius: 50%;
@ -84,7 +83,6 @@
#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-downloading"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-other-instance"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
}
@ -96,7 +94,6 @@
.panel-banner-item[notificationid="update-available"]::after,
.panel-banner-item[notificationid="update-downloading"]::after,
.panel-banner-item[notificationid="update-manual"]::after,
.panel-banner-item[notificationid="update-other-instance"]::after,
.panel-banner-item[notificationid="update-restart"]::after {
background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
border-radius: 50%;

Просмотреть файл

@ -415,7 +415,6 @@ html|*#webRTC-previewVideo {
.popup-notification-icon[popupid="update-available"],
.popup-notification-icon[popupid="update-downloading"],
.popup-notification-icon[popupid="update-manual"],
.popup-notification-icon[popupid="update-other-instance"],
.popup-notification-icon[popupid="update-restart"] {
background: #74BF43 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
border-radius: 50%;

Просмотреть файл

@ -302,7 +302,6 @@ toolbar[brighttext] {
#PanelUI-menu-button[badge-status="update-available"],
#PanelUI-menu-button[badge-status="update-downloading"],
#PanelUI-menu-button[badge-status="update-manual"],
#PanelUI-menu-button[badge-status="update-other-instance"],
#PanelUI-menu-button[badge-status="update-restart"] {
list-style-image: url("chrome://browser/skin/menu-badged.svg");
}

Просмотреть файл

@ -169,16 +169,6 @@ if defined('MOZ_UPDATER') and not IS_ANDROID:
'type': 'nsUpdateProcessor',
'headers': ['/toolkit/xre/nsUpdateDriver.h'],
},
{
'cid': '{cf4c4487-66d9-4e18-a2e9-39002245332f}',
'contract_ids': ['@mozilla.org/updates/update-sync-manager;1'],
'type': 'nsUpdateSyncManager',
'singleton': True,
'headers': ['/toolkit/xre/nsUpdateSyncManager.h'],
'constructor': 'nsUpdateSyncManager::GetSingleton',
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
'categories': {'xpcom-startup': 'nsUpdateSyncManager'},
},
]
if not defined('MOZ_DISABLE_PARENTAL_CONTROLS'):

Просмотреть файл

@ -6778,10 +6778,10 @@
"alert_emails": ["application-update-telemetry-alerts@mozilla.com", "bytesized@mozilla.com"],
"expires_in_version": "never",
"kind": "categorical",
"bug_numbers": [893505, 1521427, 1553982],
"bug_numbers": [893505, 1521427],
"releaseChannelCollection": "opt-out",
"description": "Update: the application update doorhanger type that was displayed.",
"labels": ["restart", "available", "manual", "unsupported", "otherinstance"]
"labels": ["restart", "available", "manual", "unsupported"]
},
"UPDATE_NOTIFICATION_BADGE_SHOWN": {
"record_in_processes": ["main"],
@ -6789,10 +6789,10 @@
"alert_emails": ["application-update-telemetry-alerts@mozilla.com", "bytesized@mozilla.com"],
"expires_in_version": "never",
"kind": "categorical",
"bug_numbers": [893505, 1365204, 1521427, 1553982],
"bug_numbers": [893505, 1365204, 1521427],
"releaseChannelCollection": "opt-out",
"description": "Update: the application update badge type that was displayed.",
"labels": ["restart", "available", "manual", "unsupported", "otherinstance"]
"labels": ["restart", "available", "manual", "unsupported"]
},
"UPDATE_NOTIFICATION_DISMISSED": {
"record_in_processes": ["main"],
@ -6800,10 +6800,10 @@
"alert_emails": ["application-update-telemetry-alerts@mozilla.com", "bytesized@mozilla.com"],
"expires_in_version": "never",
"kind": "categorical",
"bug_numbers": [893505, 1521427, 1553982],
"bug_numbers": [893505, 1521427],
"releaseChannelCollection": "opt-out",
"description": "Update: the dismiss action was executed for this application update doorhanger type.",
"labels": ["restart", "available", "manual", "unsupported", "otherinstance"]
"labels": ["restart", "available", "manual", "unsupported"]
},
"UPDATE_NOTIFICATION_MAIN_ACTION_DOORHANGER": {
"record_in_processes": ["main"],
@ -6811,10 +6811,10 @@
"alert_emails": ["application-update-telemetry-alerts@mozilla.com", "bytesized@mozilla.com"],
"expires_in_version": "never",
"kind": "categorical",
"bug_numbers": [893505, 1521427, 1553982],
"bug_numbers": [893505, 1521427],
"releaseChannelCollection": "opt-out",
"description": "Update: the main update action was initiated for this application update doorhanger type.",
"labels": ["restart", "available", "manual", "unsupported", "otherinstance"]
"labels": ["restart", "available", "manual", "unsupported"]
},
"UPDATE_NOTIFICATION_MAIN_ACTION_MENU": {
"record_in_processes": ["main"],
@ -6822,10 +6822,10 @@
"alert_emails": ["application-update-telemetry-alerts@mozilla.com", "bytesized@mozilla.com"],
"expires_in_version": "never",
"kind": "categorical",
"bug_numbers": [893505, 1521427, 1553982],
"bug_numbers": [893505, 1521427],
"releaseChannelCollection": "opt-out",
"description": "Update: the update action was initiated from the PanelUI application update menu item.",
"labels": ["restart", "available", "manual", "unsupported", "otherinstance"]
"labels": ["restart", "available", "manual", "unsupported"]
},
"UPDATE_CAN_USE_BITS_EXTERNAL": {
"record_in_processes": ["main"],

Просмотреть файл

@ -10,9 +10,6 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { clearTimeout, setTimeout } = ChromeUtils.import(
"resource://gre/modules/Timer.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
@ -20,13 +17,6 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/AppMenuNotifications.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"AppUpdateService",
"@mozilla.org/updates/update-service;1",
"nsIApplicationUpdateService"
);
const PREF_APP_UPDATE_UNSUPPORTED_URL = "app.update.unsupported.url";
// Setup the hamburger button badges for updates.
@ -39,7 +29,7 @@ var UpdateListener = {
init() {
// Persist the unsupported notification across sessions. If at some point an
// update is found this pref is cleared and the notification won't be shown.
// update is found this pref is cleared and the notifcation won't be shown.
let url = Services.prefs.getCharPref(PREF_APP_UPDATE_UNSUPPORTED_URL, null);
if (url) {
this.showUpdateNotification("unsupported", true, true, win =>
@ -126,9 +116,7 @@ var UpdateListener = {
const addTelemetry = id => {
// No telemetry for the "downloading" state.
if (type !== "downloading") {
// Histogram category labels can't have dashes in them.
let telemetryType = type.replaceAll("-", "");
Services.telemetry.getHistogramById(id).add(telemetryType);
Services.telemetry.getHistogramById(id).add(type);
}
};
let action = {
@ -162,11 +150,8 @@ var UpdateListener = {
}
},
showRestartNotification(update, dismissed) {
let notification = AppUpdateService.isOtherInstanceHandlingUpdates
? "other-instance"
: "restart";
this.showUpdateNotification(notification, true, dismissed, () =>
showRestartNotification(dismissed) {
this.showUpdateNotification("restart", true, dismissed, () =>
this.requestRestart()
);
},
@ -177,7 +162,10 @@ var UpdateListener = {
false,
dismissed,
() => {
AppUpdateService.downloadUpdate(update, true);
let updateService = Cc[
"@mozilla.org/updates/update-service;1"
].getService(Ci.nsIApplicationUpdateService);
updateService.downloadUpdate(update, true);
},
doc => this.replaceReleaseNotes(doc, update, "updateAvailableWhatsNew")
);
@ -265,22 +253,19 @@ var UpdateListener = {
if (badgeWaitTimeMs < doorhangerWaitTimeMs) {
this.addTimeout(badgeWaitTimeMs, () => {
// Skip the badge if we're waiting for another instance.
if (!AppUpdateService.isOtherInstanceHandlingUpdates) {
this.showRestartNotification(update, true);
}
this.showRestartNotification(true);
// doorhangerWaitTimeMs is relative to when we initially received
// the event. Since we've already waited badgeWaitTimeMs, subtract
// that from doorhangerWaitTimeMs.
let remainingTime = doorhangerWaitTimeMs - badgeWaitTimeMs;
this.addTimeout(remainingTime, () => {
this.showRestartNotification(update, false);
this.showRestartNotification(false);
});
});
} else {
this.addTimeout(doorhangerWaitTimeMs, () => {
this.showRestartNotification(update, false);
this.showRestartNotification(false);
});
}
break;

Просмотреть файл

@ -52,12 +52,6 @@ const PREF_APP_UPDATE_BITS_ENABLED = "app.update.BITS.enabled";
const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations";
const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
const PREF_APP_UPDATE_CANCELATIONS_OSX_MAX = "app.update.cancelations.osx.max";
const PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_ENABLED =
"app.update.checkOnlyInstance.enabled";
const PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_INTERVAL =
"app.update.checkOnlyInstance.interval";
const PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_TIMEOUT =
"app.update.checkOnlyInstance.timeout";
const PREF_APP_UPDATE_DISABLEDFORTESTING = "app.update.disabledForTesting";
const PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS = "app.update.download.attempts";
const PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS = "app.update.download.maxAttempts";
@ -251,18 +245,6 @@ const XML_SAVER_INTERVAL_MS = 200;
// update before proceeding anyway.
const LANGPACK_UPDATE_DEFAULT_TIMEOUT = 300000;
// Interval between rechecks for other instances after the initial check finds
// at least one other instance.
const ONLY_INSTANCE_CHECK_DEFAULT_POLL_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
// Wait this long after detecting that another instance is running (having been
// polling that entire time) before giving up and applying the update anyway.
const ONLY_INSTANCE_CHECK_DEFAULT_TIMEOUT_MS = 6 * 60 * 60 * 1000; // 6 hours
// The other instance check timeout can be overridden via a pref, but we limit
// that value to this so that the pref can't effectively disable the feature.
const ONLY_INSTANCE_CHECK_MAX_TIMEOUT_MS = 2 * 24 * 60 * 60 * 1000; // 2 days
// Object to keep track of the current phase of the update and whether there
// has been a write failure for the phase so only one telemetry ping is made
// for the phase.
@ -334,106 +316,6 @@ function unwrap(obj) {
*/
const LangPackUpdates = new WeakMap();
/**
* When we're polling to see if other running instances of the application have
* exited, there's no need to ever start polling again in parallel. To prevent
* doing that, we keep track of the promise that resolves when polling completes
* and return that if a second simultaneous poll is requested, so that the
* multiple callers end up waiting for the same promise to resolve.
*/
let gOtherInstancePollPromise;
/**
* Query the update sync manager to see if another instance of this same
* installation of this application is currently running, under the context of
* any operating system user (not just the current one).
* This function immediately returns the current, instantaneous status of any
* other instances.
*
* @return true if at least one other instance is running, false if not
*/
function isOtherInstanceRunning(callback) {
const checkEnabled = Services.prefs.getBoolPref(
PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_ENABLED,
true
);
if (!checkEnabled) {
LOG("isOtherInstanceRunning - disabled by pref, skipping check");
return false;
}
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
return syncManager.isOtherInstanceRunning();
}
/**
* Query the update sync manager to see if another instance of this same
* installation of this application is currently running, under the context of
* any operating system user (not just the one running this instance).
* This function polls for the status of other instances continually
* (asynchronously) until either none exist or a timeout expires.
*
* @return a Promise that resolves with false if at any point during polling no
* other instances can be found, or resolves with true if the timeout
* expires when other instances are still running
*/
function waitForOtherInstances() {
// If we're already in the middle of a poll, reuse it rather than start again.
if (gOtherInstancePollPromise) {
return gOtherInstancePollPromise;
}
let timeout = Services.prefs.getIntPref(
PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_TIMEOUT,
ONLY_INSTANCE_CHECK_DEFAULT_TIMEOUT_MS
);
// Don't allow the pref to set a super high timeout and break this feature.
if (timeout > ONLY_INSTANCE_CHECK_MAX_TIMEOUT_MS) {
timeout = ONLY_INSTANCE_CHECK_MAX_TIMEOUT_MS;
}
let interval = Services.prefs.getIntPref(
PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_INTERVAL,
ONLY_INSTANCE_CHECK_DEFAULT_POLL_INTERVAL_MS
);
// Don't allow an interval longer than the timeout.
interval = Math.min(interval, timeout);
let iterations = 0;
const maxIterations = Math.ceil(timeout / interval);
gOtherInstancePollPromise = new Promise(function(resolve, reject) {
let poll = function() {
iterations++;
if (!isOtherInstanceRunning()) {
LOG("waitForOtherInstances - no other instances found, exiting");
resolve(false);
gOtherInstancePollPromise = undefined;
} else if (iterations >= maxIterations) {
LOG(
"waitForOtherInstances - timeout expired while other instances " +
"are still running"
);
resolve(true);
gOtherInstancePollPromise = undefined;
} else if (iterations + 1 == maxIterations && timeout % interval != 0) {
// In case timeout isn't a multiple of interval, set the next timeout
// for the remainder of the time rather than for the usual interval.
setTimeout(poll, timeout % interval);
} else {
setTimeout(poll, interval);
}
};
LOG("waitForOtherInstances - beginning polling");
poll();
});
return gOtherInstancePollPromise;
}
/**
* Tests to make sure that we can write to a given directory.
*
@ -2464,7 +2346,7 @@ UpdateService.prototype = {
"UpdateService:_postUpdateProcessing - unable to apply " +
"updates... returning early"
);
if (hasUpdateMutex()) {
if (!this.isOtherInstanceHandlingUpdates) {
// If the update is present in the update directory somehow,
// it would prevent us from notifying the user of further updates.
cleanupUpdate();
@ -3012,8 +2894,6 @@ UpdateService.prototype = {
);
} else if (!hasUpdateMutex()) {
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_MUTEX);
} else if (isOtherInstanceRunning()) {
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_OTHER_INSTANCE);
} else if (!this.canCheckForUpdates) {
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_CHECK);
}
@ -3339,15 +3219,6 @@ UpdateService.prototype = {
return false;
}
if (isOtherInstanceRunning()) {
// This doesn't block update checks, but we will have to wait until either
// the other instance is gone or we time out waiting for it.
LOG(
"UpdateService.canCheckForUpdates - another instance is holding the " +
"semaphore, will need to wait for it prior to checking for updates"
);
}
LOG("UpdateService.canCheckForUpdates - able to check for updates");
return true;
},
@ -3363,9 +3234,7 @@ UpdateService.prototype = {
* See nsIUpdateService.idl
*/
get canApplyUpdates() {
return (
getCanApplyUpdates() && hasUpdateMutex() && !isOtherInstanceRunning()
);
return getCanApplyUpdates() && hasUpdateMutex();
},
/**
@ -3379,7 +3248,7 @@ UpdateService.prototype = {
* See nsIUpdateService.idl
*/
get isOtherInstanceHandlingUpdates() {
return !hasUpdateMutex() || isOtherInstanceRunning();
return !hasUpdateMutex();
},
/**
@ -3551,7 +3420,7 @@ UpdateService.prototype = {
this.canStageUpdates;
LOG("Elevation required: " + this.elevationRequired);
LOG(
"Other instance of the application currently running: " +
"Update being handled by other instance: " +
this.isOtherInstanceHandlingUpdates
);
LOG("Downloading: " + !!this.isDownloading);
@ -3994,7 +3863,7 @@ UpdateManager.prototype = {
/**
* See nsIUpdateService.idl
*/
refreshUpdateStatus: async function UM_refreshUpdateStatus() {
refreshUpdateStatus: function UM_refreshUpdateStatus() {
var update = this._readyUpdate;
if (!update) {
return;
@ -4231,62 +4100,47 @@ Checker.prototype = {
return;
}
waitForOtherInstances()
.then(() => this.getUpdateURL(force))
.then(url => {
if (!url) {
return;
}
this.getUpdateURL(force).then(url => {
if (!url) {
return;
}
// It's possible that another check was kicked off and that request sent
// while we were waiting for other instances to exit here; if the other
// instances were closed and also the other check was started during the
// same interval between polls, then here we could now be about to start
// a second overlapping check, which should not happen. So make sure we
// don't have a request already active before we start a new one.
if (this._request) {
LOG(
"Checker: checkForUpdates: check request already active, aborting"
);
return;
}
this._request = new XMLHttpRequest();
this._request.open("GET", url, true);
this._request.channel.notificationCallbacks = new CertUtils.BadCertHandler(
false
);
// Prevent the request from reading from the cache.
this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
// Prevent the request from writing to the cache.
this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
// Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
this._request.channel.QueryInterface(
Ci.nsIHttpChannelInternal
).beConservative = true;
this._request = new XMLHttpRequest();
this._request.open("GET", url, true);
this._request.channel.notificationCallbacks = new CertUtils.BadCertHandler(
false
);
// Prevent the request from reading from the cache.
this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
// Prevent the request from writing to the cache.
this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
// Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
this._request.channel.QueryInterface(
Ci.nsIHttpChannelInternal
).beConservative = true;
this._request.overrideMimeType("text/xml");
// The Cache-Control header is only interpreted by proxies and the
// final destination. It does not help if a resource is already
// cached locally.
this._request.setRequestHeader("Cache-Control", "no-cache");
// HTTP/1.0 servers might not implement Cache-Control and
// might only implement Pragma: no-cache
this._request.setRequestHeader("Pragma", "no-cache");
this._request.overrideMimeType("text/xml");
// The Cache-Control header is only interpreted by proxies and the
// final destination. It does not help if a resource is already
// cached locally.
this._request.setRequestHeader("Cache-Control", "no-cache");
// HTTP/1.0 servers might not implement Cache-Control and
// might only implement Pragma: no-cache
this._request.setRequestHeader("Pragma", "no-cache");
var self = this;
this._request.addEventListener("error", function(event) {
self.onError(event);
});
this._request.addEventListener("load", function(event) {
self.onLoad(event);
});
LOG("Checker:checkForUpdates - sending request to: " + url);
this._request.send(null);
this._callback = listener;
var self = this;
this._request.addEventListener("error", function(event) {
self.onError(event);
});
this._request.addEventListener("load", function(event) {
self.onLoad(event);
});
LOG("Checker:checkForUpdates - sending request to: " + url);
this._request.send(null);
this._callback = listener;
});
},
/**
@ -5693,7 +5547,7 @@ Downloader.prototype = {
let update = this._update;
promiseLangPacksUpdated(update).then(() => {
LOG(
"Downloader:onStopRequest - Notifying observers that " +
"UpdateManager:refreshUpdateStatus - Notifying observers that " +
"an update was downloaded. topic: update-downloaded, status: " +
update.state
);

Просмотреть файл

@ -92,9 +92,6 @@ var AUSTLMY = {
CHK_DISABLED_BY_POLICY: 37,
// Update check failed due to write error
CHK_ERR_WRITE_FAILURE: 38,
// Update check was delayed because another instance of the application is
// currently running
CHK_OTHER_INSTANCE: 39,
/**
* Submit a telemetry ping for the update check result code or a telemetry

Просмотреть файл

@ -425,35 +425,6 @@ interface nsIUpdateProcessor : nsISupports
void fixUpdateDirectoryPerms(in boolean useServiceOnFailure);
};
/**
* Upon creation, which should happen early during startup, the sync manager
* opens and locks a named semaphore. All other running instances of the same
* installation of the app also open the same semaphore, so we can use it to
* determine whether any other instance is running. If so, we'll temporarily
* hold off on performing update tasks until there are no other instances or
* until a timeout expires, whichever comes first. That way we can avoid
* updating behind the back of copies that are still running, so we don't force
* all running instances to restart (see bug 1366808, where an error was added
* informing the user of the need to restart any running instances that have
* been updated).
*/
[scriptable, uuid(cf4c4487-66d9-4e18-a2e9-39002245332f)]
interface nsIUpdateSyncManager : nsISupports
{
/**
* Returns whether another instance of this application is running.
* @returns true if another instance has the semaphore open, false if not
*/
bool isOtherInstanceRunning();
/**
* Should only be used for testing.
* Closes and reopens the semaphore, possibly under a different name if the
* path hash has changed (which should only happen if a test is forcing it).
*/
void resetSemaphore();
};
/**
* An interface describing a global application service that maintains a list
* of updates previously performed as well as the current active update.

Просмотреть файл

@ -81,44 +81,6 @@ add_task(async function setupTestCommon() {
],
});
// We need to keep the update semaphore from thinking two instances are
// running because of the mochitest parent instance, which means we need to
// override the directory service with a fake executable path and then reset
// the semaphore. But leaving the directory service overridden causes problems
// for these tests, so we need to restore the real service immediately after.
// To form the path, we'll use the real executable path with a token appended
// (the path needs to be absolute, but not to point to a real file).
// This block is loosely copied from adjustGeneralPaths() in another update
// test file, xpcshellUtilsAUS.js, but this is a much more limited version;
// it's been copied here both because the full function is overkill and also
// because making it general enough to run in both xpcshell and mochitest
// would have been unreasonably difficult.
let exePath = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile);
let dirProvider = {
getFile: function AGP_DP_getFile(aProp, aPersistent) {
// Set the value of persistent to false so when this directory provider is
// unregistered it will revert back to the original provider.
aPersistent.value = false;
switch (aProp) {
case XRE_EXECUTABLE_FILE:
exePath.append("browser-test");
return exePath;
}
return null;
},
QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
};
let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
ds.registerProvider(dirProvider);
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
syncManager.resetSemaphore();
ds.unregisterProvider(dirProvider);
setUpdateTimerPrefs();
reloadUpdateManagerData(true);
removeUpdateFiles(true);
@ -145,13 +107,6 @@ registerCleanupFunction(async () => {
// Always try to restore the original updater files. If none of the updater
// backup files are present then this is just a no-op.
await finishTestRestoreUpdaterBackup();
// Reset the update semaphore once again so that we know the semaphore we're
// interested in here will be closed properly (normally that happens during
// XPCOM shutdown, but that isn't consistent during tests).
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
syncManager.resetSemaphore();
});
/**

Просмотреть файл

@ -1,59 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This is the script that runs in the child xpcshell process for the test
// unit_aus_update/updateSemaphore.js.
// The main thing this script does is override the child's directory service
// so that it ends up with the same fake
// binary path that the parent test runner has opened its semaphore with.
// This requires that we have already been passed a constant on our command
// line which contains the relevant fake binary path, which is called:
/* global customExePath */
// We also need this builtin function from xpcshell itself:
/* global simulateNoScriptActivity */
print("child process is running");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
// This function is copied from xpcshellUtilsAUS.js so that we can have our
// xpcshell subprocess call it without having to load that whole file, because
// it turns out that needs a bunch of infrastructure that normally the testing
// framework would provide, and that also requires a bunch of setup, and it's
// just not worth all that. This is a cut down version that only includes the
// directory provider functionality that the subprocess really needs.
function adjustGeneralPaths() {
let dirProvider = {
getFile: function AGP_DP_getFile(aProp, aPersistent) {
// Set the value of persistent to false so when this directory provider is
// unregistered it will revert back to the original provider.
aPersistent.value = false;
// The semaphore only needs XREExeF, so that's all we provide.
if (aProp == "XREExeF") {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(customExePath);
return file;
}
return null;
},
QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
};
let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
ds.QueryInterface(Ci.nsIProperties).undefine("XREExeF");
ds.registerProvider(dirProvider);
// Now that we've overridden the directory provider, the name of the update
// semaphore needs to be changed to match the overridden path.
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
syncManager.resetSemaphore();
}
adjustGeneralPaths();
// Wait a few seconds for the parent to do what it needs to do, then exit.
print("child process should now have the semaphore; will exit in 5 seconds");
simulateNoScriptActivity(5);
print("child process exiting now");

Просмотреть файл

@ -1619,15 +1619,7 @@ function getSpecialFolderDir(aCSIDL) {
);
let aryPath = ctypes.char16_t.array()(260);
let rv = SHGetSpecialFolderPath(0, aryPath, aCSIDL, false);
if (!rv) {
do_throw(
"SHGetSpecialFolderPath failed to retrieve " +
aCSIDL +
" with Win32 error " +
ctypes.winLastError
);
}
SHGetSpecialFolderPath(0, aryPath, aCSIDL, false);
lib.close();
let path = aryPath.readString(); // Convert the c-string to js-string
@ -4354,7 +4346,6 @@ function adjustGeneralPaths() {
let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_DIR);
ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_BIN_DIR);
ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
ds.registerProvider(dirProvider);
registerCleanupFunction(function AGP_cleanup() {
debugDump("start - unregistering directory provider");
@ -4412,25 +4403,8 @@ function adjustGeneralPaths() {
ds.unregisterProvider(dirProvider);
cleanupTestCommon();
// Now that our provided is unregistered, reset the semaphore a second time
// so that we know the semaphore we're interested in gets unlocked (xpcshell
// doesn't always run a proper XPCOM shutdown sequence, which is where that
// would normally be happening).
let syncManager = Cc[
"@mozilla.org/updates/update-sync-manager;1"
].getService(Ci.nsIUpdateSyncManager);
syncManager.resetSemaphore();
debugDump("finish - unregistering directory provider");
});
// Now that we've overridden the directory provider, the name of the update
// semaphore needs to be changed to match the overridden path.
debugDump("resetting update semaphore");
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
syncManager.resetSemaphore();
}
/**

Просмотреть файл

@ -94,7 +94,6 @@ FINAL_TARGET_FILES += [
"data/partial_removed-files_mac",
"data/partial_update_manifest",
"data/replace_log_success",
"data/semaphoreTestChildScript.js",
"data/simple.mar",
"TestAUSReadStrings1.ini",
"TestAUSReadStrings2.ini",

Просмотреть файл

@ -1,99 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This test verifies that the update semaphore is working correctly by
// a) making sure we're the only one that's opened it to begin with, and then
// b) starting a second copy of the same binary and making sure we can tell we
// are no longer the only one that's opened it.
const { Subprocess } = ChromeUtils.import(
"resource://gre/modules/Subprocess.jsm"
);
// Save off the real GRE directory and binary path before we register our
// mock directory service which overrides them both.
const thisBinary = Services.dirsvc.get("XREExeF", Ci.nsIFile);
const greDir = Services.dirsvc.get("GreD", Ci.nsIFile);
add_task(async function() {
setupTestCommon();
// First check that we believe we exclusively hold the semaphore.
let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
Ci.nsIUpdateSyncManager
);
Assert.ok(
!syncManager.isOtherInstanceRunning(),
"no other instance is running yet"
);
// Now start a second copy of this xpcshell binary so that something else
// takes the same semaphore. First we'll define its command line.
// Most of the child's code is in a separate script file, so all the command
// line has to do is set up a few required path strings we need to pass
// through to the child, and then include the script file.
const args = [
"-g",
greDir.path,
"-e",
`
const customGreDirPath = "${getApplyDirFile(
DIR_RESOURCES
).path.replaceAll("\\", "\\\\")}";
const customGreBinDirPath = "${getApplyDirFile(DIR_MACOS).path.replaceAll(
"\\",
"\\\\"
)}";
const customExePath = "${getApplyDirFile(
DIR_MACOS + FILE_APP_BIN
).path.replaceAll("\\", "\\\\")}";
const customUpdDirPath = "${getMockUpdRootD().path.replaceAll(
"\\",
"\\\\"
)}";
const customOldUpdDirPath = "${getMockUpdRootD(true).path.replaceAll(
"\\",
"\\\\"
)}";
`,
"-f",
getTestDirFile("semaphoreTestChildScript.js").path,
];
// Now we can actually invoke the process.
debugDump(`launching child process at ${thisBinary.path} with args ${args}`);
Subprocess.call({
command: thisBinary.path,
arguments: args,
stderr: "stdout",
});
// It will take the new xpcshell a little time to start up, but we should see
// the effect on the semaphore within at most a few seconds.
await TestUtils.waitForCondition(
() => syncManager.isOtherInstanceRunning(),
"waiting for child process to take the semaphore"
).catch(e => {
// Rather than throwing out of waitForCondition(), catch and log the failure
// manually so that we get output that's a bit more readable.
Assert.ok(
syncManager.isOtherInstanceRunning(),
"child process has the semaphore"
);
});
// The semaphore lock should have been closed when the process exited, but
// we'll allow a little time for the OS to clean up the handle.
await TestUtils.waitForCondition(
() => !syncManager.isOtherInstanceRunning(),
"waiting for child process to give back the semaphore"
).catch(e => {
Assert.ok(
!syncManager.isOtherInstanceRunning(),
"child process has given back the semaphore"
);
});
doTestFinish();
});

Просмотреть файл

@ -36,6 +36,3 @@ reason = Update pref migration is currently Windows only
[updateDirectoryMigrate.js]
skip-if = os != 'win'
reason = Update directory migration is currently Windows only
[updateSemaphore.js]
skip-if = os == 'mac'
reason = The semaphore isn't implemented for Mac

Просмотреть файл

@ -1,196 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "GlobalSemaphore.h"
#include "commonupdatedir.h" // for GetInstallHash
#include "mozilla/UniquePtr.h"
#include "updatedefines.h" // for NS_t* definitions
#ifndef XP_WIN
# include <fcntl.h> // for O_CREAT
# include <sys/stat.h> // for mode constants
#else
# include <limits.h> // for _XOPEN_PATH_MAX / _POSIX_PATH_MAX
#endif
#ifdef XP_WIN
# define SEMAPHORE_NAME_PREFIX "Global\\"
#else
# define SEMAPHORE_NAME_PREFIX "/"
#endif
namespace mozilla {
// Prevent this function failing the build for being unused on Mac.
#ifndef XP_MACOSX
static bool GetSemaphoreName(const char* nameToken, const char16_t* installPath,
mozilla::UniquePtr<NS_tchar[]>& semName) {
mozilla::UniquePtr<NS_tchar[]> pathHash;
if (!GetInstallHash(installPath, MOZ_APP_VENDOR, pathHash)) {
return false;
}
size_t semNameLen = strlen(SEMAPHORE_NAME_PREFIX) + strlen(nameToken) +
NS_tstrlen(pathHash.get()) + 1;
semName = mozilla::MakeUnique<NS_tchar[]>(semNameLen + 1);
if (!semName) {
return false;
}
// On Windows, we need to convert the token string to UTF-16.
// printf can do that for us, but we need to tell it to by changing the type
// indicator in the format string.
// We also need different prefixes to the semaphore name, because it's a
// path-like string and because Windows has the "global" concept.
# ifdef XP_WIN
const NS_tchar* kSemaphoreNameFormat = NS_T(SEMAPHORE_NAME_PREFIX "%S-%s");
# else
const NS_tchar* kSemaphoreNameFormat = NS_T(SEMAPHORE_NAME_PREFIX "%s-%s");
# endif
NS_tsnprintf(semName.get(), semNameLen + 1, kSemaphoreNameFormat, nameToken,
pathHash.get());
return true;
}
#endif
#ifdef XP_WIN
GlobalSemHandle OpenGlobalSemaphore(const char* nameToken,
const char16_t* installPath) {
mozilla::UniquePtr<NS_tchar[]> semName;
if (!GetSemaphoreName(nameToken, installPath, semName)) {
return nullptr;
}
// Initialize the semaphore to LONG_MAX because we're not actually using it
// to control access to any limited resource, we just want to know how many
// times the semaphore is taken. And taking a semaphore decrements it, so we
// have to start at the top of the range instead of at zero.
GlobalSemHandle sem =
::CreateSemaphoreW(nullptr, LONG_MAX, LONG_MAX, semName.get());
if (sem) {
// Claim a reference to the semaphore. After this point, the semaphore count
// should always be LONG_MAX - [number of running instances].
if (::WaitForSingleObject(sem, 0) != WAIT_OBJECT_0) {
// Either there are LONG_MAX instances running or something is wrong and
// this semaphore is not usable.
::CloseHandle(sem);
sem = nullptr;
}
}
return sem;
}
void ReleaseGlobalSemaphore(GlobalSemHandle sem) {
if (sem) {
::ReleaseSemaphore(sem, 1, nullptr);
::CloseHandle(sem);
}
}
bool IsOtherInstanceRunning(GlobalSemHandle sem, bool* aResult) {
// There's no documented way to get a semaphore's current count except to
// wait on it (which decrements it) and then release it (which increments it
// back and returns the pre-incremented count), so that's what we'll do. There
// is also NtQuerySemaphore, but using the native API doesn't seem worth the
// trouble here.
if (sem && ::WaitForSingleObject(sem, 0) == WAIT_OBJECT_0) {
LONG count = 0;
if (::ReleaseSemaphore(sem, 1, &count)) {
// At rest, the count is (LONG_MAX - [number of running instances]), but
// the count we read had been
// decremented once more by the Wait call here, so we need to compare
// against one less than that to see if there's exactly one instance
// running.
*aResult = (count != (LONG_MAX - 2));
return true;
}
}
return false;
}
#elif defined(XP_MACOSX)
// We don't actually have Mac support here, because sem_getvalue isn't supported
// there (the header calls it deprecated, but actually it's just unimplemented),
// and there is no other way to get the value of a POSIX-semaphore. We'll have
// to do something platform-specific.
GlobalSemHandle OpenGlobalSemaphore(const char* /* unused */,
const char16_t* /* unused */) {
// Return something that isn't a valid pointer, but that the manager won't
// think represents a failure.
return (GlobalSemHandle)1;
}
void ReleaseGlobalSemaphore(GlobalSemHandle /* unused */) {}
bool IsOtherInstanceRunning(GlobalSemHandle /* unused */, bool* aResult) {
*aResult = false;
return true;
}
#else // Neither Windows nor Mac
GlobalSemHandle OpenGlobalSemaphore(const char* nameToken,
const char16_t* installPath) {
mozilla::UniquePtr<NS_tchar[]> semName;
if (!GetSemaphoreName(nameToken, installPath, semName)) {
return nullptr;
}
// Initialize the semaphore to the maximum value because we don't actually
// want to limit the number of instances, and assign all permissions because
// we also don't want to lock out any other users. The update service has
// protections to prevent problems resulting from this semaphore
// being messed with by an attacker.
GlobalSemHandle sem = sem_open(semName.get(), O_CREAT,
S_IRWXU | S_IRWXG | S_IRWXO, SEM_VALUE_MAX);
if (sem == SEM_FAILED) {
return nullptr;
}
// Decrement the semaphore whether we created it or opened it. This way the
// count is always SEM_VALUE_MAX - [number of running instances].
if (sem_trywait(sem)) {
// Either there are SEM_VALUE_MAX instances running or something is wrong
// and this semaphore is not usable.
sem_close(sem);
return nullptr;
}
return sem;
}
void ReleaseGlobalSemaphore(GlobalSemHandle sem) {
if (sem) {
sem_post(sem);
sem_close(sem);
}
}
bool IsOtherInstanceRunning(GlobalSemHandle sem, bool* aResult) {
int value = 0;
if (sem && !sem_getvalue(sem, &value)) {
// Zero and negative values are all error states.
if (value <= 0) {
return false;
}
*aResult = (value != (SEM_VALUE_MAX - 1));
return true;
}
return false;
}
#endif
}; // namespace mozilla

Просмотреть файл

@ -1,45 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef GLOBALSEMAPHORE_H
#define GLOBALSEMAPHORE_H
namespace mozilla {
#ifdef XP_WIN
# include <windows.h>
using GlobalSemHandle = HANDLE;
#else
# include <semaphore.h>
using GlobalSemHandle = sem_t*;
#endif
/*
* nameToken should be a string very briefly naming the semaphore you are
* creating, and it should be unique systemwide except for across multiple
* instances of the same application.
* installPath should be the path to the directory containing the application.
*
* Taken together, those two parameters will be used to form a string which is
* unique to this copy of this application.
* Creating the semaphore will fail if the final string (the token plus the path
* hash) is longer than the platform's maximum. On Windows that's MAX_PATH, on
* POSIX systems it's either _POSIX_PATH_MAX or perhaps SEM_NAME_LEN.
*
* Returns nullptr upon failure, on all platforms.
*/
GlobalSemHandle OpenGlobalSemaphore(const char* nameToken,
const char16_t* installPath);
void ReleaseGlobalSemaphore(GlobalSemHandle sem);
// aResult will be set to true if another instance *was* found, false if not.
// Return value is true on success, false on error (and aResult won't be set).
bool IsOtherInstanceRunning(GlobalSemHandle sem, bool* aResult);
}; // namespace mozilla
#endif // GLOBALEMAPHORE_H

Просмотреть файл

@ -38,7 +38,6 @@ EXPORTS.mozilla += [
"AutoSQLiteLifetime.h",
"Bootstrap.h",
"CmdLineAndEnvUtils.h",
"GlobalSemaphore.h",
"SafeMode.h",
"UntrustedModulesData.h",
]
@ -126,11 +125,6 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
"nsAndroidStartup.cpp",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
UNIFIED_SOURCES += [
"GlobalSemaphore.cpp",
]
UNIFIED_SOURCES += [
"/toolkit/mozapps/update/common/commonupdatedir.cpp",
"AutoSQLiteLifetime.cpp",
@ -169,7 +163,6 @@ if CONFIG["MOZ_UPDATER"]:
if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
UNIFIED_SOURCES += [
"nsUpdateDriver.cpp",
"nsUpdateSyncManager.cpp",
]
if CONFIG["MOZ_PDF_PRINTING"]:

Просмотреть файл

@ -3,18 +3,14 @@
* 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/. */
#include "nsAppStartupNotifier.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsICategoryManager.h"
#include "nsIObserver.h"
#include "nsXPCOM.h"
#include "nsAppStartupNotifier.h"
#include "mozilla/SimpleEnumerator.h"
using namespace mozilla;
/* static */
nsresult nsAppStartupNotifier::NotifyObservers(const char* aCategory) {
NS_ENSURE_ARG(aCategory);

Просмотреть файл

@ -7,7 +7,6 @@
#define nsAppStartupNotifier_h___
#include "nsIAppStartupNotifier.h"
#include "nsError.h"
class nsAppStartupNotifier final {
public:

Просмотреть файл

@ -55,10 +55,6 @@
using namespace mozilla;
static LazyLogModule sUpdateLog("updatedriver");
// Some other file in our unified batch might have defined LOG already.
#ifdef LOG
# undef LOG
#endif
#define LOG(args) MOZ_LOG(sUpdateLog, mozilla::LogLevel::Debug, args)
#ifdef XP_WIN

Просмотреть файл

@ -1,128 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "nsUpdateSyncManager.h"
#include "mozilla/Unused.h"
#include "mozilla/Services.h"
#include "nsComponentManagerUtils.h"
#include "nsCRT.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsIProperties.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#define UPDATE_SEMAPHORE_NAME_TOKEN "MozillaUpdateSemaphore"
nsUpdateSyncManager* gUpdateSyncManager = nullptr;
NS_IMPL_ISUPPORTS(nsUpdateSyncManager, nsIUpdateSyncManager, nsIObserver)
nsUpdateSyncManager::nsUpdateSyncManager() { OpenSemaphore(); }
nsUpdateSyncManager::~nsUpdateSyncManager() {
ReleaseSemaphore();
gUpdateSyncManager = nullptr;
}
already_AddRefed<nsUpdateSyncManager> nsUpdateSyncManager::GetSingleton() {
if (!gUpdateSyncManager) {
gUpdateSyncManager = new nsUpdateSyncManager();
}
return do_AddRef(gUpdateSyncManager);
}
NS_IMETHODIMP nsUpdateSyncManager::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) {
mozilla::Unused << aSubject;
mozilla::Unused << aData;
// We want to hold the semaphore for as much of the lifetime of the app
// as we can, so we observe xpcom-startup so we get constructed as early as
// possible, which triggers constructing the singleton.
if (!nsCRT::strcmp(aTopic, NS_XPCOM_STARTUP_OBSERVER_ID)) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
return observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
false);
}
return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
ReleaseSemaphore();
}
return NS_OK;
}
nsresult nsUpdateSyncManager::OpenSemaphore() {
if (mSemaphore) {
// Semaphore is already open.
return NS_OK;
}
// Only open the semaphore from the browser process.
// Our component registration should already have made sure of this.
if (NS_WARN_IF(XRE_GetProcessType() != GeckoProcessType_Default)) {
return NS_OK;
}
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(dirSvc, NS_ERROR_SERVICE_NOT_AVAILABLE);
nsCOMPtr<nsIFile> appFile;
nsresult rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
getter_AddRefs(appFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> appDirFile;
rv = appFile->GetParent(getter_AddRefs(appDirFile));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString appDirPath;
rv = appDirFile->GetPath(appDirPath);
NS_ENSURE_SUCCESS(rv, rv);
mSemaphore = mozilla::OpenGlobalSemaphore(
UPDATE_SEMAPHORE_NAME_TOKEN, PromiseFlatString(appDirPath).get());
NS_ENSURE_TRUE(mSemaphore, NS_ERROR_FAILURE);
return NS_OK;
}
void nsUpdateSyncManager::ReleaseSemaphore() {
if (!mSemaphore) {
// Semaphore is already released.
return;
}
mozilla::ReleaseGlobalSemaphore(mSemaphore);
mSemaphore = nullptr;
}
NS_IMETHODIMP nsUpdateSyncManager::IsOtherInstanceRunning(bool* aResult) {
if (NS_WARN_IF(XRE_GetProcessType() != GeckoProcessType_Default)) {
return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
if (!mSemaphore) {
return NS_ERROR_NOT_INITIALIZED;
}
bool rv = mozilla::IsOtherInstanceRunning(mSemaphore, aResult);
NS_ENSURE_TRUE(rv, NS_ERROR_FAILURE);
return NS_OK;
}
NS_IMETHODIMP nsUpdateSyncManager::ResetSemaphore() {
ReleaseSemaphore();
return OpenSemaphore();
}

Просмотреть файл

@ -1,63 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef nsUpdateSyncManager_h__
#define nsUpdateSyncManager_h__
#include "mozilla/AlreadyAddRefed.h"
#include "nsIObserver.h"
#include "nsIUpdateService.h"
#include "GlobalSemaphore.h"
// The update sync manager is responsible for making sure that only one
// instance of the application is running at the time we want to start updating
// it. It does this by taking a semaphore very early during the application's
// startup process. Then, when app update tasks are ready to run, the update
// service asks us whether anything else has also taken the semaphore, which,
// if true, would mean another instance of the application is currently running
// and performing update tasks should be avoided (the update service also runs
// a timeout and eventually goes ahead with the update in order to prevent an
// external program from effectively disabling updates).
// The immediately obvious tool for this job would be a mutex and not a
// semaphore, since only one instance can be applying updates at a time, but at
// it turns out that wouldn't quite meet the requirements. Consider this
// scenario: an application instance we'll call instance A runs and takes the
// mutex. It doesn't check for updates right away. A second instance called B
// then starts and cannot get the mutex during its startup because instance A
// still holds it. A third instance C is started and has the same problem. Now,
// what if instance A exits? It returns the mutex, so if either B or C decide to
// check for updates they'll be able to take it. But neither is aware of the
// other's existence, so whichever one wins that race will be able to apply an
// update behind the other one's back, which is the exact thing this component
// is intended to prevent. By using a semaphore instead, every instance is
// always aware of how many other instances are running by checking the
// semaphore's count, and this problem is avoided.
class nsUpdateSyncManager final : public nsIUpdateSyncManager,
public nsIObserver {
public:
nsUpdateSyncManager();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIUPDATESYNCMANAGER
NS_DECL_NSIOBSERVER
static already_AddRefed<nsUpdateSyncManager> GetSingleton();
private:
~nsUpdateSyncManager();
nsUpdateSyncManager(nsUpdateSyncManager&) = delete;
nsUpdateSyncManager(nsUpdateSyncManager&&) = delete;
nsUpdateSyncManager& operator=(nsUpdateSyncManager&) = delete;
nsUpdateSyncManager& operator=(nsUpdateSyncManager&&) = delete;
nsresult OpenSemaphore();
void ReleaseSemaphore();
mozilla::GlobalSemHandle mSemaphore = nullptr;
};
#endif // nsUpdateSyncManager_h__