mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-18 21:27:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
278
toolkit/profile/ProfileUnlockerWin.cpp
Normal file
278
toolkit/profile/ProfileUnlockerWin.cpp
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "ProfileUnlockerWin.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* RAII class to obtain and manage a handle to a Restart Manager session.
|
||||
* It opens a new handle upon construction and releases it upon destruction.
|
||||
*/
|
||||
class MOZ_STACK_CLASS ScopedRestartManagerSession
|
||||
{
|
||||
public:
|
||||
explicit ScopedRestartManagerSession(ProfileUnlockerWin& aUnlocker)
|
||||
: mError(ERROR_INVALID_HANDLE)
|
||||
, mHandle((DWORD)-1) // 0 is a valid restart manager handle
|
||||
, mUnlocker(aUnlocker)
|
||||
{
|
||||
mError = mUnlocker.StartSession(mHandle);
|
||||
}
|
||||
|
||||
~ScopedRestartManagerSession()
|
||||
{
|
||||
if (mError == ERROR_SUCCESS) {
|
||||
mUnlocker.EndSession(mHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the handle is a valid Restart Ranager handle.
|
||||
*/
|
||||
inline bool
|
||||
ok()
|
||||
{
|
||||
return mError == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Restart Manager handle to pass to other Restart Manager APIs.
|
||||
*/
|
||||
inline DWORD
|
||||
handle()
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD mError;
|
||||
DWORD mHandle;
|
||||
ProfileUnlockerWin& mUnlocker;
|
||||
};
|
||||
|
||||
ProfileUnlockerWin::ProfileUnlockerWin(const nsAString& aFileName)
|
||||
: mRmStartSession(nullptr)
|
||||
, mRmRegisterResources(nullptr)
|
||||
, mRmGetList(nullptr)
|
||||
, mRmEndSession(nullptr)
|
||||
, mQueryFullProcessImageName(nullptr)
|
||||
, mFileName(aFileName)
|
||||
{
|
||||
}
|
||||
|
||||
ProfileUnlockerWin::~ProfileUnlockerWin()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ProfileUnlockerWin, nsIProfileUnlocker)
|
||||
|
||||
nsresult
|
||||
ProfileUnlockerWin::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mRestartMgrModule);
|
||||
if (mFileName.IsEmpty()) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsModuleHandle module(::LoadLibraryW(L"Rstrtmgr.dll"));
|
||||
if (!module) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
mRmStartSession =
|
||||
reinterpret_cast<RMSTARTSESSION>(::GetProcAddress(module, "RmStartSession"));
|
||||
if (!mRmStartSession) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mRmRegisterResources =
|
||||
reinterpret_cast<RMREGISTERRESOURCES>(::GetProcAddress(module,
|
||||
"RmRegisterResources"));
|
||||
if (!mRmRegisterResources) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mRmGetList = reinterpret_cast<RMGETLIST>(::GetProcAddress(module,
|
||||
"RmGetList"));
|
||||
if (!mRmGetList) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mRmEndSession = reinterpret_cast<RMENDSESSION>(::GetProcAddress(module,
|
||||
"RmEndSession"));
|
||||
if (!mRmEndSession) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mQueryFullProcessImageName =
|
||||
reinterpret_cast<QUERYFULLPROCESSIMAGENAME>(::GetProcAddress(
|
||||
::GetModuleHandleW(L"kernel32.dll"),
|
||||
"QueryFullProcessImageNameW"));
|
||||
if (!mQueryFullProcessImageName) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mRestartMgrModule.steal(module);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DWORD
|
||||
ProfileUnlockerWin::StartSession(DWORD& aHandle)
|
||||
{
|
||||
WCHAR sessionKey[CCH_RM_SESSION_KEY + 1] = {0};
|
||||
return mRmStartSession(&aHandle, 0, sessionKey);
|
||||
}
|
||||
|
||||
void
|
||||
ProfileUnlockerWin::EndSession(DWORD aHandle)
|
||||
{
|
||||
mRmEndSession(aHandle);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ProfileUnlockerWin::Unlock(uint32_t aSeverity)
|
||||
{
|
||||
if (!mRestartMgrModule) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (aSeverity != FORCE_QUIT) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ScopedRestartManagerSession session(*this);
|
||||
if (!session.ok()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
LPCWSTR resources[] = { mFileName.get() };
|
||||
DWORD error = mRmRegisterResources(session.handle(), 1, resources, 0, nullptr,
|
||||
0, nullptr);
|
||||
if (error != ERROR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Using a AutoTArray here because we expect the required size to be 1.
|
||||
AutoTArray<RM_PROCESS_INFO, 1> info;
|
||||
UINT numEntries;
|
||||
UINT numEntriesNeeded = 1;
|
||||
error = ERROR_MORE_DATA;
|
||||
DWORD reason = RmRebootReasonNone;
|
||||
while (error == ERROR_MORE_DATA) {
|
||||
info.SetLength(numEntriesNeeded);
|
||||
numEntries = numEntriesNeeded;
|
||||
error = mRmGetList(session.handle(), &numEntriesNeeded, &numEntries,
|
||||
&info[0], &reason);
|
||||
}
|
||||
if (error != ERROR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (numEntries == 0) {
|
||||
// Nobody else is locking the file; the other process must have terminated
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
for (UINT i = 0; i < numEntries; ++i) {
|
||||
rv = TryToTerminate(info[i].Process);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing could be unlocked then we return the error code of the last
|
||||
// failure that was returned.
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProfileUnlockerWin::TryToTerminate(RM_UNIQUE_PROCESS& aProcess)
|
||||
{
|
||||
// Subtle: If the target process terminated before this call to OpenProcess,
|
||||
// this call will still succeed. This is because the restart manager session
|
||||
// internally retains a handle to the target process. The rules for Windows
|
||||
// PIDs state that the PID of a terminated process remains valid as long as
|
||||
// at least one handle to that process remains open, so when we reach this
|
||||
// point the PID is still valid and the process will open successfully.
|
||||
DWORD accessRights = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE;
|
||||
nsAutoHandle otherProcess(::OpenProcess(accessRights, FALSE,
|
||||
aProcess.dwProcessId));
|
||||
if (!otherProcess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FILETIME creationTime, exitTime, kernelTime, userTime;
|
||||
if (!::GetProcessTimes(otherProcess, &creationTime, &exitTime, &kernelTime,
|
||||
&userTime)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (::CompareFileTime(&aProcess.ProcessStartTime, &creationTime)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
WCHAR imageName[MAX_PATH];
|
||||
DWORD imageNameLen = MAX_PATH;
|
||||
if (!mQueryFullProcessImageName(otherProcess, 0, imageName, &imageNameLen)) {
|
||||
// The error codes for this function are not very descriptive. There are
|
||||
// actually two failure cases here: Either the call failed because the
|
||||
// process is no longer running, or it failed for some other reason. We
|
||||
// need to know which case that is.
|
||||
DWORD otherProcessExitCode;
|
||||
if (!::GetExitCodeProcess(otherProcess, &otherProcessExitCode) ||
|
||||
otherProcessExitCode == STILL_ACTIVE) {
|
||||
// The other process is still running.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// The other process must have terminated. We should return NS_OK so that
|
||||
// this process may proceed with startup.
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIFile> otherProcessImageName;
|
||||
if (NS_FAILED(NS_NewLocalFile(nsDependentString(imageName, imageNameLen),
|
||||
false, getter_AddRefs(otherProcessImageName)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsAutoString otherProcessLeafName;
|
||||
if (NS_FAILED(otherProcessImageName->GetLeafName(otherProcessLeafName))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
imageNameLen = MAX_PATH;
|
||||
if (!mQueryFullProcessImageName(::GetCurrentProcess(), 0, imageName,
|
||||
&imageNameLen)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIFile> thisProcessImageName;
|
||||
if (NS_FAILED(NS_NewLocalFile(nsDependentString(imageName, imageNameLen),
|
||||
false, getter_AddRefs(thisProcessImageName)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsAutoString thisProcessLeafName;
|
||||
if (NS_FAILED(thisProcessImageName->GetLeafName(thisProcessLeafName))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Make sure the image leaf names match
|
||||
if (_wcsicmp(otherProcessLeafName.get(), thisProcessLeafName.get())) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// We know that another process holds the lock and that it shares the same
|
||||
// image name as our process. Let's kill it.
|
||||
// Subtle: TerminateProcess returning ERROR_ACCESS_DENIED is actually an
|
||||
// indicator that the target process managed to shut down on its own. In that
|
||||
// case we should return NS_OK since we may proceed with startup.
|
||||
if (!::TerminateProcess(otherProcess, 1) &&
|
||||
::GetLastError() != ERROR_ACCESS_DENIED) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
60
toolkit/profile/ProfileUnlockerWin.h
Normal file
60
toolkit/profile/ProfileUnlockerWin.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 ProfileUnlockerWin_h
|
||||
#define ProfileUnlockerWin_h
|
||||
|
||||
#include <windows.h>
|
||||
#include <restartmanager.h>
|
||||
|
||||
#include "nsIProfileUnlocker.h"
|
||||
#include "nsProfileStringTypes.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ProfileUnlockerWin final : public nsIProfileUnlocker
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPROFILEUNLOCKER
|
||||
|
||||
explicit ProfileUnlockerWin(const nsAString& aFileName);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
DWORD StartSession(DWORD& aHandle);
|
||||
void EndSession(DWORD aHandle);
|
||||
|
||||
private:
|
||||
~ProfileUnlockerWin();
|
||||
nsresult TryToTerminate(RM_UNIQUE_PROCESS& aProcess);
|
||||
|
||||
private:
|
||||
typedef DWORD (WINAPI *RMSTARTSESSION)(DWORD*, DWORD, WCHAR[]);
|
||||
typedef DWORD (WINAPI *RMREGISTERRESOURCES)(DWORD, UINT, LPCWSTR[], UINT,
|
||||
RM_UNIQUE_PROCESS[], UINT,
|
||||
LPCWSTR[]);
|
||||
typedef DWORD (WINAPI *RMGETLIST)(DWORD, UINT*, UINT*, RM_PROCESS_INFO[],
|
||||
LPDWORD);
|
||||
typedef DWORD (WINAPI *RMENDSESSION)(DWORD);
|
||||
typedef BOOL (WINAPI *QUERYFULLPROCESSIMAGENAME)(HANDLE, DWORD, LPWSTR, PDWORD);
|
||||
|
||||
private:
|
||||
nsModuleHandle mRestartMgrModule;
|
||||
RMSTARTSESSION mRmStartSession;
|
||||
RMREGISTERRESOURCES mRmRegisterResources;
|
||||
RMGETLIST mRmGetList;
|
||||
RMENDSESSION mRmEndSession;
|
||||
QUERYFULLPROCESSIMAGENAME mQueryFullProcessImageName;
|
||||
|
||||
nsString mFileName;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ProfileUnlockerWin_h
|
||||
|
||||
225
toolkit/profile/content/createProfileWizard.js
Normal file
225
toolkit/profile/content/createProfileWizard.js
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/* 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/. */
|
||||
|
||||
const C = Components.classes;
|
||||
const I = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
const ToolkitProfileService = "@mozilla.org/toolkit/profile-service;1";
|
||||
|
||||
var gProfileService;
|
||||
var gProfileManagerBundle;
|
||||
|
||||
var gDefaultProfileParent;
|
||||
|
||||
// The directory where the profile will be created.
|
||||
var gProfileRoot;
|
||||
|
||||
// Text node to display the location and name of the profile to create.
|
||||
var gProfileDisplay;
|
||||
|
||||
// Called once when the wizard is opened.
|
||||
function initWizard()
|
||||
{
|
||||
try {
|
||||
gProfileService = C[ToolkitProfileService].getService(I.nsIToolkitProfileService);
|
||||
gProfileManagerBundle = document.getElementById("bundle_profileManager");
|
||||
|
||||
var dirService = C["@mozilla.org/file/directory_service;1"].getService(I.nsIProperties);
|
||||
gDefaultProfileParent = dirService.get("DefProfRt", I.nsIFile);
|
||||
|
||||
// Initialize the profile location display.
|
||||
gProfileDisplay = document.getElementById("profileDisplay").firstChild;
|
||||
setDisplayToDefaultFolder();
|
||||
}
|
||||
catch (e) {
|
||||
window.close();
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
|
||||
// Called every time the second wizard page is displayed.
|
||||
function initSecondWizardPage()
|
||||
{
|
||||
var profileName = document.getElementById("profileName");
|
||||
profileName.select();
|
||||
profileName.focus();
|
||||
|
||||
// Initialize profile name validation.
|
||||
checkCurrentInput(profileName.value);
|
||||
}
|
||||
|
||||
const kSaltTable = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0' ];
|
||||
|
||||
var kSaltString = "";
|
||||
for (var i = 0; i < 8; ++i) {
|
||||
kSaltString += kSaltTable[Math.floor(Math.random() * kSaltTable.length)];
|
||||
}
|
||||
|
||||
|
||||
function saltName(aName)
|
||||
{
|
||||
return kSaltString + "." + aName;
|
||||
}
|
||||
|
||||
function setDisplayToDefaultFolder()
|
||||
{
|
||||
var defaultProfileDir = gDefaultProfileParent.clone();
|
||||
defaultProfileDir.append(saltName(document.getElementById("profileName").value));
|
||||
gProfileRoot = defaultProfileDir;
|
||||
document.getElementById("useDefault").disabled = true;
|
||||
}
|
||||
|
||||
function updateProfileDisplay()
|
||||
{
|
||||
gProfileDisplay.data = gProfileRoot.path;
|
||||
}
|
||||
|
||||
// Invoke a folder selection dialog for choosing the directory of profile storage.
|
||||
function chooseProfileFolder()
|
||||
{
|
||||
var newProfileRoot;
|
||||
|
||||
var dirChooser = C["@mozilla.org/filepicker;1"].createInstance(I.nsIFilePicker);
|
||||
dirChooser.init(window, gProfileManagerBundle.getString("chooseFolder"),
|
||||
I.nsIFilePicker.modeGetFolder);
|
||||
dirChooser.appendFilters(I.nsIFilePicker.filterAll);
|
||||
|
||||
// default to the Profiles folder
|
||||
dirChooser.displayDirectory = gDefaultProfileParent;
|
||||
|
||||
dirChooser.show();
|
||||
newProfileRoot = dirChooser.file;
|
||||
|
||||
// Disable the "Default Folder..." button when the default profile folder
|
||||
// was selected manually in the File Picker.
|
||||
document.getElementById("useDefault").disabled =
|
||||
(newProfileRoot.parent.equals(gDefaultProfileParent));
|
||||
|
||||
gProfileRoot = newProfileRoot;
|
||||
updateProfileDisplay();
|
||||
}
|
||||
|
||||
// Checks the current user input for validity and triggers an error message accordingly.
|
||||
function checkCurrentInput(currentInput)
|
||||
{
|
||||
var finishButton = document.documentElement.getButton("finish");
|
||||
var finishText = document.getElementById("finishText");
|
||||
var canAdvance;
|
||||
|
||||
var errorMessage = checkProfileName(currentInput);
|
||||
|
||||
if (!errorMessage) {
|
||||
finishText.className = "";
|
||||
if (AppConstants.platform == "macosx") {
|
||||
finishText.firstChild.data = gProfileManagerBundle.getString("profileFinishTextMac");
|
||||
}
|
||||
else {
|
||||
finishText.firstChild.data = gProfileManagerBundle.getString("profileFinishText");
|
||||
}
|
||||
canAdvance = true;
|
||||
}
|
||||
else {
|
||||
finishText.className = "error";
|
||||
finishText.firstChild.data = errorMessage;
|
||||
canAdvance = false;
|
||||
}
|
||||
|
||||
document.documentElement.canAdvance = canAdvance;
|
||||
finishButton.disabled = !canAdvance;
|
||||
|
||||
updateProfileDisplay();
|
||||
|
||||
return canAdvance;
|
||||
}
|
||||
|
||||
function updateProfileName(aNewName)
|
||||
{
|
||||
if (checkCurrentInput(aNewName)) {
|
||||
gProfileRoot.leafName = saltName(aNewName);
|
||||
updateProfileDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether the given string is a valid profile name.
|
||||
// Returns an error message describing the error in the name or "" when it's valid.
|
||||
function checkProfileName(profileNameToCheck)
|
||||
{
|
||||
// Check for emtpy profile name.
|
||||
if (!/\S/.test(profileNameToCheck))
|
||||
return gProfileManagerBundle.getString("profileNameEmpty");
|
||||
|
||||
// Check whether all characters in the profile name are allowed.
|
||||
if (/([\\*:?<>|\/\"])/.test(profileNameToCheck))
|
||||
return gProfileManagerBundle.getFormattedString("invalidChar", [RegExp.$1]);
|
||||
|
||||
// Check whether a profile with the same name already exists.
|
||||
if (profileExists(profileNameToCheck))
|
||||
return gProfileManagerBundle.getString("profileExists");
|
||||
|
||||
// profileNameToCheck is valid.
|
||||
return "";
|
||||
}
|
||||
|
||||
function profileExists(aName)
|
||||
{
|
||||
var profiles = gProfileService.profiles;
|
||||
while (profiles.hasMoreElements()) {
|
||||
var profile = profiles.getNext().QueryInterface(I.nsIToolkitProfile);
|
||||
if (profile.name.toLowerCase() == aName.toLowerCase())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called when the first wizard page is shown.
|
||||
function enableNextButton()
|
||||
{
|
||||
document.documentElement.canAdvance = true;
|
||||
}
|
||||
|
||||
function onFinish()
|
||||
{
|
||||
var profileName = document.getElementById("profileName").value;
|
||||
var profile;
|
||||
|
||||
// Create profile named profileName in profileRoot.
|
||||
try {
|
||||
profile = gProfileService.createProfile(gProfileRoot, profileName);
|
||||
}
|
||||
catch (e) {
|
||||
var profileCreationFailed =
|
||||
gProfileManagerBundle.getString("profileCreationFailed");
|
||||
var profileCreationFailedTitle =
|
||||
gProfileManagerBundle.getString("profileCreationFailedTitle");
|
||||
var promptService = C["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(I.nsIPromptService);
|
||||
promptService.alert(window, profileCreationFailedTitle,
|
||||
profileCreationFailed + "\n" + e);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// window.opener is false if the Create Profile Wizard was opened from the
|
||||
// command line.
|
||||
if (window.opener) {
|
||||
// Add new profile to the list in the Profile Manager.
|
||||
window.opener.CreateProfile(profile);
|
||||
}
|
||||
else {
|
||||
// Use the newly created Profile.
|
||||
var profileLock = profile.lock(null);
|
||||
|
||||
var dialogParams = window.arguments[0].QueryInterface(I.nsIDialogParamBlock);
|
||||
dialogParams.objects.insertElementAt(profileLock, 0, false);
|
||||
}
|
||||
|
||||
// Exit the wizard.
|
||||
return true;
|
||||
}
|
||||
74
toolkit/profile/content/createProfileWizard.xul
Normal file
74
toolkit/profile/content/createProfileWizard.xul
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
|
||||
<!DOCTYPE wizard [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % profileDTD SYSTEM "chrome://mozapps/locale/profile/createProfileWizard.dtd">
|
||||
%profileDTD;
|
||||
]>
|
||||
|
||||
<wizard id="createProfileWizard"
|
||||
title="&newprofile.title;"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onwizardfinish="return onFinish();"
|
||||
onload="initWizard();"
|
||||
style="&window.size;">
|
||||
|
||||
<stringbundle id="bundle_profileManager"
|
||||
src="chrome://mozapps/locale/profile/profileSelection.properties"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://mozapps/content/profile/createProfileWizard.js"/>
|
||||
|
||||
<wizardpage id="explanation" onpageshow="enableNextButton();">
|
||||
<description>&profileCreationExplanation_1.text;</description>
|
||||
<description>&profileCreationExplanation_2.text;</description>
|
||||
<description>&profileCreationExplanation_3.text;</description>
|
||||
<spacer flex="1"/>
|
||||
#ifdef XP_MACOSX
|
||||
<description>&profileCreationExplanation_4Mac.text;</description>
|
||||
#else
|
||||
#ifdef XP_UNIX
|
||||
<description>&profileCreationExplanation_4Gnome.text;</description>
|
||||
#else
|
||||
<description>&profileCreationExplanation_4.text;</description>
|
||||
#endif
|
||||
#endif
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="createProfile" onpageshow="initSecondWizardPage();">
|
||||
<description>&profileCreationIntro.text;</description>
|
||||
|
||||
<label accesskey="&profilePrompt.accesskey;" control="ProfileName">&profilePrompt.label;</label>
|
||||
<textbox id="profileName" value="&profileDefaultName;"
|
||||
oninput="updateProfileName(this.value);"/>
|
||||
|
||||
<separator/>
|
||||
|
||||
<description>&profileDirectoryExplanation.text;</description>
|
||||
|
||||
<vbox class="indent" flex="1" style="overflow: auto;">
|
||||
<description id="profileDisplay">*</description>
|
||||
</vbox>
|
||||
|
||||
<hbox>
|
||||
<button label="&button.choosefolder.label;" oncommand="chooseProfileFolder();"
|
||||
accesskey="&button.choosefolder.accesskey;"/>
|
||||
|
||||
<button id="useDefault" label="&button.usedefault.label;"
|
||||
oncommand="setDisplayToDefaultFolder(); updateProfileDisplay();"
|
||||
accesskey="&button.usedefault.accesskey;" disabled="true"/>
|
||||
</hbox>
|
||||
|
||||
<separator/>
|
||||
|
||||
<description id="finishText">*</description>
|
||||
</wizardpage>
|
||||
|
||||
</wizard>
|
||||
269
toolkit/profile/content/profileSelection.js
Normal file
269
toolkit/profile/content/profileSelection.js
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const C = Components.classes;
|
||||
const I = Components.interfaces;
|
||||
|
||||
const ToolkitProfileService = "@mozilla.org/toolkit/profile-service;1";
|
||||
|
||||
var gDialogParams;
|
||||
var gProfileManagerBundle;
|
||||
var gBrandBundle;
|
||||
var gProfileService;
|
||||
|
||||
function startup()
|
||||
{
|
||||
try {
|
||||
gDialogParams = window.arguments[0].
|
||||
QueryInterface(I.nsIDialogParamBlock);
|
||||
|
||||
gProfileService = C[ToolkitProfileService].getService(I.nsIToolkitProfileService);
|
||||
|
||||
gProfileManagerBundle = document.getElementById("bundle_profileManager");
|
||||
gBrandBundle = document.getElementById("bundle_brand");
|
||||
|
||||
document.documentElement.centerWindowOnScreen();
|
||||
|
||||
var profilesElement = document.getElementById("profiles");
|
||||
|
||||
var profileList = gProfileService.profiles;
|
||||
while (profileList.hasMoreElements()) {
|
||||
var profile = profileList.getNext().QueryInterface(I.nsIToolkitProfile);
|
||||
|
||||
var listitem = profilesElement.appendItem(profile.name, "");
|
||||
|
||||
var tooltiptext =
|
||||
gProfileManagerBundle.getFormattedString("profileTooltip", [profile.name, profile.rootDir.path]);
|
||||
listitem.setAttribute("tooltiptext", tooltiptext);
|
||||
listitem.setAttribute("class", "listitem-iconic");
|
||||
listitem.profile = profile;
|
||||
try {
|
||||
if (profile === gProfileService.selectedProfile) {
|
||||
setTimeout(function(a) {
|
||||
profilesElement.ensureElementIsVisible(a);
|
||||
profilesElement.selectItem(a);
|
||||
}, 0, listitem);
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
|
||||
var autoSelectLastProfile = document.getElementById("autoSelectLastProfile");
|
||||
autoSelectLastProfile.checked = gProfileService.startWithLastProfile;
|
||||
profilesElement.focus();
|
||||
}
|
||||
catch (e) {
|
||||
window.close();
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
|
||||
function acceptDialog()
|
||||
{
|
||||
var appName = gBrandBundle.getString("brandShortName");
|
||||
|
||||
var profilesElement = document.getElementById("profiles");
|
||||
var selectedProfile = profilesElement.selectedItem;
|
||||
if (!selectedProfile) {
|
||||
var pleaseSelectTitle = gProfileManagerBundle.getString("pleaseSelectTitle");
|
||||
var pleaseSelect =
|
||||
gProfileManagerBundle.getFormattedString("pleaseSelect", [appName]);
|
||||
Services.prompt.alert(window, pleaseSelectTitle, pleaseSelect);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var profileLock;
|
||||
|
||||
try {
|
||||
profileLock = selectedProfile.profile.lock({ value: null });
|
||||
}
|
||||
catch (e) {
|
||||
if (!selectedProfile.profile.rootDir.exists()) {
|
||||
var missingTitle = gProfileManagerBundle.getString("profileMissingTitle");
|
||||
var missing =
|
||||
gProfileManagerBundle.getFormattedString("profileMissing", [appName]);
|
||||
Services.prompt.alert(window, missingTitle, missing);
|
||||
return false;
|
||||
}
|
||||
|
||||
var lockedTitle = gProfileManagerBundle.getString("profileLockedTitle");
|
||||
var locked =
|
||||
gProfileManagerBundle.getFormattedString("profileLocked2", [appName, selectedProfile.profile.name, appName]);
|
||||
Services.prompt.alert(window, lockedTitle, locked);
|
||||
|
||||
return false;
|
||||
}
|
||||
gDialogParams.objects.insertElementAt(profileLock.nsIProfileLock, 0, false);
|
||||
|
||||
gProfileService.selectedProfile = selectedProfile.profile;
|
||||
gProfileService.defaultProfile = selectedProfile.profile;
|
||||
updateStartupPrefs();
|
||||
|
||||
gDialogParams.SetInt(0, 1);
|
||||
|
||||
gDialogParams.SetString(0, selectedProfile.profile.name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function exitDialog()
|
||||
{
|
||||
updateStartupPrefs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateStartupPrefs()
|
||||
{
|
||||
var autoSelectLastProfile = document.getElementById("autoSelectLastProfile");
|
||||
gProfileService.startWithLastProfile = autoSelectLastProfile.checked;
|
||||
|
||||
/* Bug 257777 */
|
||||
gProfileService.startOffline = document.getElementById("offlineState").checked;
|
||||
}
|
||||
|
||||
// handle key event on listboxes
|
||||
function onProfilesKey(aEvent)
|
||||
{
|
||||
switch ( aEvent.keyCode )
|
||||
{
|
||||
case KeyEvent.DOM_VK_BACK_SPACE:
|
||||
if (AppConstants.platform != "macosx")
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DELETE:
|
||||
ConfirmDelete();
|
||||
break;
|
||||
case KeyEvent.DOM_VK_F2:
|
||||
RenameProfile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onProfilesDblClick(aEvent)
|
||||
{
|
||||
if (aEvent.target.localName == "listitem")
|
||||
document.documentElement.acceptDialog();
|
||||
}
|
||||
|
||||
// invoke the createProfile Wizard
|
||||
function CreateProfileWizard()
|
||||
{
|
||||
window.openDialog('chrome://mozapps/content/profile/createProfileWizard.xul',
|
||||
'', 'centerscreen,chrome,modal,titlebar', gProfileService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from createProfileWizard to update the display.
|
||||
*/
|
||||
function CreateProfile(aProfile)
|
||||
{
|
||||
var profilesElement = document.getElementById("profiles");
|
||||
|
||||
var listitem = profilesElement.appendItem(aProfile.name, "");
|
||||
|
||||
var tooltiptext =
|
||||
gProfileManagerBundle.getFormattedString("profileTooltip", [aProfile.name, aProfile.rootDir.path]);
|
||||
listitem.setAttribute("tooltiptext", tooltiptext);
|
||||
listitem.setAttribute("class", "listitem-iconic");
|
||||
listitem.profile = aProfile;
|
||||
|
||||
profilesElement.ensureElementIsVisible(listitem);
|
||||
profilesElement.selectItem(listitem);
|
||||
}
|
||||
|
||||
// rename the selected profile
|
||||
function RenameProfile()
|
||||
{
|
||||
var profilesElement = document.getElementById("profiles");
|
||||
var selectedItem = profilesElement.selectedItem;
|
||||
if (!selectedItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var selectedProfile = selectedItem.profile;
|
||||
|
||||
var oldName = selectedProfile.name;
|
||||
var newName = {value: oldName};
|
||||
|
||||
var dialogTitle = gProfileManagerBundle.getString("renameProfileTitle");
|
||||
var msg =
|
||||
gProfileManagerBundle.getFormattedString("renameProfilePrompt", [oldName]);
|
||||
|
||||
if (Services.prompt.prompt(window, dialogTitle, msg, newName, null, {value:0})) {
|
||||
newName = newName.value;
|
||||
|
||||
// User hasn't changed the profile name. Treat as if cancel was pressed.
|
||||
if (newName == oldName)
|
||||
return false;
|
||||
|
||||
try {
|
||||
selectedProfile.name = newName;
|
||||
}
|
||||
catch (e) {
|
||||
var alTitle = gProfileManagerBundle.getString("profileNameInvalidTitle");
|
||||
var alMsg = gProfileManagerBundle.getFormattedString("profileNameInvalid", [newName]);
|
||||
Services.prompt.alert(window, alTitle, alMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedItem.label = newName;
|
||||
var tiptext = gProfileManagerBundle.
|
||||
getFormattedString("profileTooltip",
|
||||
[newName, selectedProfile.rootDir.path]);
|
||||
selectedItem.setAttribute("tooltiptext", tiptext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function ConfirmDelete()
|
||||
{
|
||||
var deleteButton = document.getElementById("delbutton");
|
||||
var profileList = document.getElementById( "profiles" );
|
||||
|
||||
var selectedItem = profileList.selectedItem;
|
||||
if (!selectedItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var selectedProfile = selectedItem.profile;
|
||||
var deleteFiles = false;
|
||||
|
||||
if (selectedProfile.rootDir.exists()) {
|
||||
var dialogTitle = gProfileManagerBundle.getString("deleteTitle");
|
||||
var dialogText =
|
||||
gProfileManagerBundle.getFormattedString("deleteProfileConfirm",
|
||||
[selectedProfile.rootDir.path]);
|
||||
|
||||
var buttonPressed = Services.prompt.confirmEx(window, dialogTitle, dialogText,
|
||||
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
|
||||
(Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
|
||||
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2),
|
||||
gProfileManagerBundle.getString("dontDeleteFiles"),
|
||||
null,
|
||||
gProfileManagerBundle.getString("deleteFiles"),
|
||||
null, {value:0});
|
||||
if (buttonPressed == 1)
|
||||
return false;
|
||||
|
||||
if (buttonPressed == 2)
|
||||
deleteFiles = true;
|
||||
}
|
||||
|
||||
selectedProfile.remove(deleteFiles);
|
||||
profileList.removeChild(selectedItem);
|
||||
if (profileList.firstChild != undefined) {
|
||||
profileList.selectItem(profileList.firstChild);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
70
toolkit/profile/content/profileSelection.xul
Normal file
70
toolkit/profile/content/profileSelection.xul
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
|
||||
<!--
|
||||
|
||||
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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/profile/profileSelection.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % profileDTD SYSTEM "chrome://mozapps/locale/profile/profileSelection.dtd">
|
||||
%profileDTD;
|
||||
]>
|
||||
|
||||
<dialog
|
||||
id="profileWindow"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
class="non-resizable"
|
||||
title="&windowtitle.label;"
|
||||
orient="vertical"
|
||||
buttons="accept,cancel"
|
||||
style="width: 30em;"
|
||||
onload="startup();"
|
||||
ondialogaccept="return acceptDialog()"
|
||||
ondialogcancel="return exitDialog()"
|
||||
buttonlabelaccept="&start.label;"
|
||||
buttonlabelcancel="&exit.label;">
|
||||
|
||||
<stringbundle id="bundle_profileManager"
|
||||
src="chrome://mozapps/locale/profile/profileSelection.properties"/>
|
||||
<stringbundle id="bundle_brand"
|
||||
src="chrome://branding/locale/brand.properties"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://mozapps/content/profile/profileSelection.js"/>
|
||||
|
||||
<description class="label">&pmDescription.label;</description>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox class="profile-box indent" flex="1">
|
||||
|
||||
<vbox id="managebuttons">
|
||||
<button id="newbutton" label="&newButton.label;"
|
||||
accesskey="&newButton.accesskey;" oncommand="CreateProfileWizard();"/>
|
||||
<button id="renbutton" label="&renameButton.label;"
|
||||
accesskey="&renameButton.accesskey;" oncommand="RenameProfile();"/>
|
||||
<button id="delbutton" label="&deleteButton.label;"
|
||||
accesskey="&deleteButton.accesskey;" oncommand="ConfirmDelete();"/>
|
||||
</vbox>
|
||||
|
||||
<separator flex="1"/>
|
||||
|
||||
<vbox flex="1">
|
||||
<listbox id="profiles" rows="5" seltype="single"
|
||||
ondblclick="onProfilesDblClick(event)"
|
||||
onkeypress="onProfilesKey(event);">
|
||||
</listbox>
|
||||
|
||||
<!-- Bug 257777 -->
|
||||
<checkbox id="offlineState" label="&offlineState.label;" accesskey="&offlineState.accesskey;"/>
|
||||
|
||||
<checkbox id="autoSelectLastProfile" label="&useSelected.label;"
|
||||
accesskey="&useSelected.accesskey;"/>
|
||||
</vbox>
|
||||
|
||||
</hbox>
|
||||
</dialog>
|
||||
116
toolkit/profile/gtest/TestProfileLock.cpp
Normal file
116
toolkit/profile/gtest/TestProfileLock.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsProfileLock.h"
|
||||
#include "nsString.h"
|
||||
|
||||
static void CleanParentLock(const char *tmpdir)
|
||||
{
|
||||
// nsProfileLock doesn't clean up all its files
|
||||
char permanent_lockfile[] = "/.parentlock";
|
||||
|
||||
char * parentlock_name;
|
||||
size_t parentlock_name_size = strlen(tmpdir) + strlen(permanent_lockfile) + 1;
|
||||
parentlock_name = (char*)malloc(parentlock_name_size);
|
||||
|
||||
strcpy(parentlock_name, tmpdir);
|
||||
strcat(parentlock_name, permanent_lockfile);
|
||||
|
||||
EXPECT_EQ(0, unlink(parentlock_name));
|
||||
EXPECT_EQ(0, rmdir(tmpdir));
|
||||
free(parentlock_name);
|
||||
}
|
||||
|
||||
TEST(ProfileLock, BasicLock)
|
||||
{
|
||||
char templ[] = "/tmp/profilelocktest.XXXXXX";
|
||||
char * tmpdir = mkdtemp(templ);
|
||||
ASSERT_NE(tmpdir, nullptr);
|
||||
|
||||
// This scope exits so the nsProfileLock destructor
|
||||
// can clean up the files it leaves in tmpdir.
|
||||
{
|
||||
nsProfileLock myLock;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
|
||||
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
||||
|
||||
rv = dir->InitWithNativePath(nsCString(tmpdir));
|
||||
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
||||
|
||||
rv = myLock.Lock(dir, nullptr);
|
||||
EXPECT_EQ(NS_SUCCEEDED(rv), true);
|
||||
}
|
||||
|
||||
CleanParentLock(tmpdir);
|
||||
}
|
||||
|
||||
TEST(ProfileLock, RetryLock)
|
||||
{
|
||||
char templ[] = "/tmp/profilelocktest.XXXXXX";
|
||||
char * tmpdir = mkdtemp(templ);
|
||||
ASSERT_NE(tmpdir, nullptr);
|
||||
|
||||
{
|
||||
nsProfileLock myLock;
|
||||
nsProfileLock myLock2;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
|
||||
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
||||
|
||||
rv = dir->InitWithNativePath(nsCString(tmpdir));
|
||||
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
||||
|
||||
int eventfd_fd = eventfd(0, 0);
|
||||
ASSERT_GE(eventfd_fd, 0);
|
||||
|
||||
// fcntl advisory locks are per process, so it's hard
|
||||
// to avoid using fork here.
|
||||
pid_t childpid = fork();
|
||||
|
||||
if (childpid) {
|
||||
// parent
|
||||
|
||||
// blocking read causing us to lose the race
|
||||
eventfd_t value;
|
||||
EXPECT_EQ(0, eventfd_read(eventfd_fd, &value));
|
||||
|
||||
rv = myLock2.Lock(dir, nullptr);
|
||||
EXPECT_EQ(NS_ERROR_FILE_ACCESS_DENIED, rv);
|
||||
|
||||
// kill the child
|
||||
EXPECT_EQ(0, kill(childpid, SIGTERM));
|
||||
|
||||
// reap zombie (required to collect the lock)
|
||||
int status;
|
||||
EXPECT_EQ(childpid, waitpid(childpid, &status, 0));
|
||||
|
||||
rv = myLock2.Lock(dir, nullptr);
|
||||
EXPECT_EQ(NS_SUCCEEDED(rv), true);
|
||||
} else {
|
||||
// child
|
||||
rv = myLock.Lock(dir, nullptr);
|
||||
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
||||
|
||||
// unblock parent
|
||||
EXPECT_EQ(0, eventfd_write(eventfd_fd, 1));
|
||||
|
||||
// parent will kill us
|
||||
for (;;)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
close(eventfd_fd);
|
||||
}
|
||||
|
||||
CleanParentLock(tmpdir);
|
||||
}
|
||||
16
toolkit/profile/gtest/moz.build
Normal file
16
toolkit/profile/gtest/moz.build
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Linux':
|
||||
UNIFIED_SOURCES += [
|
||||
'TestProfileLock.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
9
toolkit/profile/jar.mn
Normal file
9
toolkit/profile/jar.mn
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# 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/.
|
||||
|
||||
toolkit.jar:
|
||||
content/mozapps/profile/createProfileWizard.js (content/createProfileWizard.js)
|
||||
* content/mozapps/profile/createProfileWizard.xul (content/createProfileWizard.xul)
|
||||
content/mozapps/profile/profileSelection.js (content/profileSelection.js)
|
||||
content/mozapps/profile/profileSelection.xul (content/profileSelection.xul)
|
||||
43
toolkit/profile/moz.build
Normal file
43
toolkit/profile/moz.build
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
DIRS += ['gtest']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIProfileMigrator.idl',
|
||||
'nsIProfileUnlocker.idl',
|
||||
'nsIToolkitProfile.idl',
|
||||
'nsIToolkitProfileService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'toolkitprofile'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsProfileLock.cpp'
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
UNIFIED_SOURCES += [
|
||||
'ProfileUnlockerWin.cpp'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsToolkitProfileService.cpp'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../xre',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Startup and Profile System')
|
||||
61
toolkit/profile/notifications.txt
Normal file
61
toolkit/profile/notifications.txt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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/.
|
||||
|
||||
nsIObserver topics for profile changing. Profile changing happens in phases
|
||||
in the order given below. An observer may register separately for each phase
|
||||
of the process depending on its needs.
|
||||
|
||||
"profile-change-teardown"
|
||||
All async activity must be stopped in this phase. Typically,
|
||||
the application level observer will close all open windows.
|
||||
This is the last phase in which the subject's vetoChange()
|
||||
method may still be called.
|
||||
The next notification will be either
|
||||
profile-change-teardown-veto or profile-before-change.
|
||||
|
||||
"profile-before-change"
|
||||
Called before the profile has changed. Use this notification
|
||||
to prepare for the profile going away. If a component is
|
||||
holding any state which needs to be flushed to a profile-relative
|
||||
location, it should be done here.
|
||||
|
||||
"profile-do-change"
|
||||
Called after the profile has changed. Do the work to
|
||||
respond to having a new profile. Any change which
|
||||
affects others must be done in this phase.
|
||||
|
||||
"profile-after-change"
|
||||
Called after the profile has changed. Use this notification
|
||||
to make changes that are dependent on what some other listener
|
||||
did during its profile-do-change. For example, to respond to
|
||||
new preferences.
|
||||
|
||||
"profile-initial-state"
|
||||
Called after all phases of a change have completed. Typically
|
||||
in this phase, an application level observer will open a new window.
|
||||
|
||||
Contexts for profile changes. These are passed as the someData param to the
|
||||
observer's Observe() method.
|
||||
|
||||
"startup"
|
||||
Going from no profile to a profile.
|
||||
The following topics happen in this context:
|
||||
profile-do-change
|
||||
profile-after-change
|
||||
|
||||
"shutdown-persist"
|
||||
The user is logging out and whatever data the observer stores
|
||||
for the current profile should be released from memory and
|
||||
saved to disk.
|
||||
The following topics happen in this context:
|
||||
profile-change-net-teardown
|
||||
profile-change-teardown
|
||||
profile-before-change
|
||||
|
||||
See https://wiki.mozilla.org/XPCOM_Shutdown for more details about the shutdown
|
||||
process.
|
||||
|
||||
NOTE: Long ago there was be a "shutdown-cleanse" version of shutdown which was
|
||||
intended to clear profile data. This is no longer sent and observer code should
|
||||
remove support for it.
|
||||
69
toolkit/profile/nsIProfileMigrator.idl
Normal file
69
toolkit/profile/nsIProfileMigrator.idl
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
interface nsIFile;
|
||||
|
||||
/**
|
||||
* Helper interface for nsIProfileMigrator.
|
||||
*
|
||||
* @provider Toolkit (Startup code)
|
||||
* @client Application (Profile-migration code)
|
||||
* @obtainable nsIProfileMigrator.migrate
|
||||
*/
|
||||
[scriptable, uuid(048e5ca1-0eb7-4bb1-a9a2-a36f7d4e0e3c)]
|
||||
interface nsIProfileStartup : nsISupports
|
||||
{
|
||||
/**
|
||||
* The root directory of the semi-current profile, during profile migration.
|
||||
* After nsIProfileMigrator.migrate has returned, this object will not be
|
||||
* useful.
|
||||
*/
|
||||
readonly attribute nsIFile directory;
|
||||
|
||||
/**
|
||||
* Do profile-startup by setting NS_APP_USER_PROFILE_50_DIR in the directory
|
||||
* service and notifying the profile-startup observer topics.
|
||||
*/
|
||||
void doStartup();
|
||||
};
|
||||
|
||||
/**
|
||||
* Migrate application settings from an outside source.
|
||||
*
|
||||
* @provider Application (Profile-migration code)
|
||||
* @client Toolkit (Startup code)
|
||||
* @obtainable service, contractid("@mozilla.org/toolkit/profile-migrator;1")
|
||||
*/
|
||||
[scriptable, uuid(3df284a5-2258-4d46-a664-761ecdc04c22)]
|
||||
interface nsIProfileMigrator : nsISupports
|
||||
{
|
||||
/**
|
||||
* Migrate data from an outside source, if possible. Does nothing
|
||||
* otherwise.
|
||||
*
|
||||
* When this method is called, a default profile has been created;
|
||||
* XPCOM has been initialized such that compreg.dat is in the
|
||||
* profile; the directory service does *not* return a key for
|
||||
* NS_APP_USER_PROFILE_50_DIR or any of the keys depending on an active
|
||||
* profile. To figure out the directory of the "current" profile, use
|
||||
* aStartup.directory.
|
||||
*
|
||||
* If your migrator needs to access services that use the profile (to
|
||||
* set profile prefs or bookmarks, for example), use aStartup.doStartup.
|
||||
*
|
||||
* @param aStartup nsIProfileStartup object to use during migration.
|
||||
* @param aKey optional key of a migrator to use to skip source selection.
|
||||
* @param aProfileName optional name of the profile to use for migration.
|
||||
*
|
||||
* @note The startup code ignores COM exceptions thrown from this method.
|
||||
*/
|
||||
void migrate(in nsIProfileStartup aStartup, in ACString aKey,
|
||||
[optional] in ACString aProfileName);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_PROFILEMIGRATOR_CONTRACTID "@mozilla.org/toolkit/profile-migrator;1"
|
||||
%}
|
||||
21
toolkit/profile/nsIProfileUnlocker.idl
Normal file
21
toolkit/profile/nsIProfileUnlocker.idl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(08923af1-e7a3-4fae-ba02-128502193994)]
|
||||
interface nsIProfileUnlocker : nsISupports
|
||||
{
|
||||
const unsigned long ATTEMPT_QUIT = 0;
|
||||
const unsigned long FORCE_QUIT = 1;
|
||||
|
||||
/**
|
||||
* Try to unlock the specified profile by attempting or forcing the
|
||||
* process that currently holds the lock to quit.
|
||||
*
|
||||
* @param aSeverity either ATTEMPT_QUIT or FORCE_QUIT
|
||||
* @throws NS_ERROR_FAILURE if unlocking failed.
|
||||
*/
|
||||
void unlock(in unsigned long aSeverity);
|
||||
};
|
||||
89
toolkit/profile/nsIToolkitProfile.idl
Normal file
89
toolkit/profile/nsIToolkitProfile.idl
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsIFile;
|
||||
interface nsIProfileUnlocker;
|
||||
|
||||
/**
|
||||
* Hold on to a profile lock. Once you release the last reference to this
|
||||
* interface, the profile lock is released.
|
||||
*/
|
||||
[scriptable, uuid(7c58c703-d245-4864-8d75-9648ca4a6139)]
|
||||
interface nsIProfileLock : nsISupports
|
||||
{
|
||||
/**
|
||||
* The main profile directory.
|
||||
*/
|
||||
readonly attribute nsIFile directory;
|
||||
|
||||
/**
|
||||
* A directory corresponding to the main profile directory that exists for
|
||||
* the purpose of storing data on the local filesystem, including cache
|
||||
* files or other data files that may not represent critical user data.
|
||||
* (e.g., this directory may not be included as part of a backup scheme.)
|
||||
*
|
||||
* In some cases, this directory may just be the main profile directory.
|
||||
*/
|
||||
readonly attribute nsIFile localDirectory;
|
||||
|
||||
/**
|
||||
* The timestamp of an existing profile lock at lock time.
|
||||
*/
|
||||
readonly attribute PRTime replacedLockTime;
|
||||
|
||||
/**
|
||||
* Unlock the profile.
|
||||
*/
|
||||
void unlock();
|
||||
};
|
||||
|
||||
/**
|
||||
* A interface representing a profile.
|
||||
* @note THIS INTERFACE SHOULD BE IMPLEMENTED BY THE TOOLKIT CODE ONLY! DON'T
|
||||
* EVEN THINK ABOUT IMPLEMENTING THIS IN JAVASCRIPT!
|
||||
*/
|
||||
[scriptable, uuid(7422b090-4a86-4407-972e-75468a625388)]
|
||||
interface nsIToolkitProfile : nsISupports
|
||||
{
|
||||
/**
|
||||
* The location of the profile directory.
|
||||
*/
|
||||
readonly attribute nsIFile rootDir;
|
||||
|
||||
/**
|
||||
* The location of the profile local directory, which may be the same as
|
||||
* the root directory. See nsIProfileLock::localDirectory.
|
||||
*/
|
||||
readonly attribute nsIFile localDir;
|
||||
|
||||
/**
|
||||
* The name of the profile.
|
||||
*/
|
||||
attribute AUTF8String name;
|
||||
|
||||
/**
|
||||
* Removes the profile from the registry of profiles.
|
||||
*
|
||||
* @param removeFiles
|
||||
* Indicates whether or not the profile directory should be
|
||||
* removed in addition.
|
||||
*/
|
||||
void remove(in boolean removeFiles);
|
||||
|
||||
/**
|
||||
* Lock this profile using platform-specific locking methods.
|
||||
*
|
||||
* @param lockFile If locking fails, this may return a lockFile object
|
||||
* which can be used in platform-specific ways to
|
||||
* determine which process has the file locked. Null
|
||||
* may be passed.
|
||||
* @return An interface which holds a profile lock as long as you reference
|
||||
* it.
|
||||
* @throws NS_ERROR_FILE_ACCESS_DENIED if the profile was already locked.
|
||||
*/
|
||||
nsIProfileLock lock(out nsIProfileUnlocker aUnlocker);
|
||||
};
|
||||
108
toolkit/profile/nsIToolkitProfileService.idl
Normal file
108
toolkit/profile/nsIToolkitProfileService.idl
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsIFile;
|
||||
interface nsIToolkitProfile;
|
||||
interface nsIProfileLock;
|
||||
|
||||
[scriptable, uuid(1947899b-f369-48fa-89da-f7c37bb1e6bc)]
|
||||
interface nsIToolkitProfileService : nsISupports
|
||||
{
|
||||
attribute boolean startWithLastProfile;
|
||||
attribute boolean startOffline;
|
||||
|
||||
readonly attribute nsISimpleEnumerator /*nsIToolkitProfile*/ profiles;
|
||||
|
||||
/**
|
||||
* The currently selected profile (the one used or about to be used by the
|
||||
* browser).
|
||||
*/
|
||||
attribute nsIToolkitProfile selectedProfile;
|
||||
|
||||
/**
|
||||
* The default profile (the one used or about to be used by the
|
||||
* browser if no other profile is specified at runtime). This is the profile
|
||||
* marked with Default=1 in profiles.ini and is usually the same as
|
||||
* selectedProfile, except on Developer Edition.
|
||||
*
|
||||
* Developer Edition uses a profile named "dev-edition-default" as the
|
||||
* default profile (which it creates if it doesn't exist), unless a special
|
||||
* empty file named "ignore-dev-edition-profile" is present next to
|
||||
* profiles.ini. In that case Developer Edition behaves the same as any
|
||||
* other build of Firefox.
|
||||
*/
|
||||
attribute nsIToolkitProfile defaultProfile;
|
||||
|
||||
/**
|
||||
* Get a profile by name. This is mainly for use by the -P
|
||||
* commandline flag.
|
||||
*
|
||||
* @param aName The profile name to find.
|
||||
*/
|
||||
nsIToolkitProfile getProfileByName(in AUTF8String aName);
|
||||
|
||||
/**
|
||||
* Lock an arbitrary path as a profile. If the path does not exist, it
|
||||
* will be created and the defaults copied from the application directory.
|
||||
*/
|
||||
nsIProfileLock lockProfilePath(in nsIFile aDirectory,
|
||||
in nsIFile aTempDirectory);
|
||||
|
||||
/**
|
||||
* Create a new profile.
|
||||
*
|
||||
* The profile temporary directory will be chosen based on where the
|
||||
* profile directory is located.
|
||||
*
|
||||
* @param aRootDir
|
||||
* The profile directory. May be null, in which case a suitable
|
||||
* default will be chosen based on the profile name.
|
||||
* @param aName
|
||||
* The profile name.
|
||||
*/
|
||||
nsIToolkitProfile createProfile(in nsIFile aRootDir,
|
||||
in AUTF8String aName);
|
||||
|
||||
/**
|
||||
* Create the default profile for an application.
|
||||
*
|
||||
* The profile will be typically in
|
||||
* {Application Data}/.profilename/{salt}.default or
|
||||
* {Application Data}/.appname/{salt}.default
|
||||
* or if aVendorName is provided
|
||||
* {Application Data}/.vendor/appname/{salt}.default
|
||||
*
|
||||
* @note Either aProfileName or aAppName must be non-empty
|
||||
*
|
||||
* @param aProfileName
|
||||
* The name of the profile
|
||||
* @param aAppName
|
||||
* The name of the application
|
||||
* @param aVendorName
|
||||
* The name of the vendor
|
||||
* @return The created profile.
|
||||
*/
|
||||
nsIToolkitProfile createDefaultProfileForApp(in AUTF8String aProfileName,
|
||||
in AUTF8String aAppName,
|
||||
in AUTF8String aVendorName);
|
||||
|
||||
/**
|
||||
* Returns the number of profiles.
|
||||
* @return 0, 1, or 2. More than 2 profiles will always return 2.
|
||||
*/
|
||||
readonly attribute unsigned long profileCount;
|
||||
|
||||
/**
|
||||
* Flush the profiles list file.
|
||||
*/
|
||||
void flush();
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_PROFILESERVICE_CONTRACTID "@mozilla.org/toolkit/profile-service;1"
|
||||
%}
|
||||
661
toolkit/profile/nsProfileLock.cpp
Normal file
661
toolkit/profile/nsProfileLock.cpp
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsProfileStringTypes.h"
|
||||
#include "nsProfileLock.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include "ProfileUnlockerWin.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#endif
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef XP_UNIX
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include "prnetdb.h"
|
||||
#include "prsystem.h"
|
||||
#include "prprf.h"
|
||||
#include "prenv.h"
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && !defined(MOZ_CRASHREPORTER)
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
// **********************************************************************
|
||||
// class nsProfileLock
|
||||
//
|
||||
// This code was moved from profile/src/nsProfileAccess.
|
||||
// **********************************************************************
|
||||
|
||||
#if defined (XP_UNIX)
|
||||
static bool sDisableSignalHandling = false;
|
||||
#endif
|
||||
|
||||
nsProfileLock::nsProfileLock() :
|
||||
mHaveLock(false),
|
||||
mReplacedLockTime(0)
|
||||
#if defined (XP_WIN)
|
||||
,mLockFileHandle(INVALID_HANDLE_VALUE)
|
||||
#elif defined (XP_UNIX)
|
||||
,mPidLockFileName(nullptr)
|
||||
,mLockFileDesc(-1)
|
||||
#endif
|
||||
{
|
||||
#if defined (XP_UNIX)
|
||||
next = prev = this;
|
||||
sDisableSignalHandling = PR_GetEnv("MOZ_DISABLE_SIG_HANDLER") ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
nsProfileLock::nsProfileLock(nsProfileLock& src)
|
||||
{
|
||||
*this = src;
|
||||
}
|
||||
|
||||
|
||||
nsProfileLock& nsProfileLock::operator=(nsProfileLock& rhs)
|
||||
{
|
||||
Unlock();
|
||||
|
||||
mHaveLock = rhs.mHaveLock;
|
||||
rhs.mHaveLock = false;
|
||||
|
||||
#if defined (XP_WIN)
|
||||
mLockFileHandle = rhs.mLockFileHandle;
|
||||
rhs.mLockFileHandle = INVALID_HANDLE_VALUE;
|
||||
#elif defined (XP_UNIX)
|
||||
mLockFileDesc = rhs.mLockFileDesc;
|
||||
rhs.mLockFileDesc = -1;
|
||||
mPidLockFileName = rhs.mPidLockFileName;
|
||||
rhs.mPidLockFileName = nullptr;
|
||||
if (mPidLockFileName)
|
||||
{
|
||||
// rhs had a symlink lock, therefore it was on the list.
|
||||
PR_REMOVE_LINK(&rhs);
|
||||
PR_APPEND_LINK(this, &mPidLockList);
|
||||
}
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsProfileLock::~nsProfileLock()
|
||||
{
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
#if defined (XP_UNIX)
|
||||
|
||||
static int setupPidLockCleanup;
|
||||
|
||||
PRCList nsProfileLock::mPidLockList =
|
||||
PR_INIT_STATIC_CLIST(&nsProfileLock::mPidLockList);
|
||||
|
||||
void nsProfileLock::RemovePidLockFiles(bool aFatalSignal)
|
||||
{
|
||||
while (!PR_CLIST_IS_EMPTY(&mPidLockList))
|
||||
{
|
||||
nsProfileLock *lock = static_cast<nsProfileLock*>(mPidLockList.next);
|
||||
lock->Unlock(aFatalSignal);
|
||||
}
|
||||
}
|
||||
|
||||
static struct sigaction SIGHUP_oldact;
|
||||
static struct sigaction SIGINT_oldact;
|
||||
static struct sigaction SIGQUIT_oldact;
|
||||
static struct sigaction SIGILL_oldact;
|
||||
static struct sigaction SIGABRT_oldact;
|
||||
static struct sigaction SIGSEGV_oldact;
|
||||
static struct sigaction SIGTERM_oldact;
|
||||
|
||||
void nsProfileLock::FatalSignalHandler(int signo
|
||||
#ifdef SA_SIGINFO
|
||||
, siginfo_t *info, void *context
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Remove any locks still held.
|
||||
RemovePidLockFiles(true);
|
||||
|
||||
// Chain to the old handler, which may exit.
|
||||
struct sigaction *oldact = nullptr;
|
||||
|
||||
switch (signo) {
|
||||
case SIGHUP:
|
||||
oldact = &SIGHUP_oldact;
|
||||
break;
|
||||
case SIGINT:
|
||||
oldact = &SIGINT_oldact;
|
||||
break;
|
||||
case SIGQUIT:
|
||||
oldact = &SIGQUIT_oldact;
|
||||
break;
|
||||
case SIGILL:
|
||||
oldact = &SIGILL_oldact;
|
||||
break;
|
||||
case SIGABRT:
|
||||
oldact = &SIGABRT_oldact;
|
||||
break;
|
||||
case SIGSEGV:
|
||||
oldact = &SIGSEGV_oldact;
|
||||
break;
|
||||
case SIGTERM:
|
||||
oldact = &SIGTERM_oldact;
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("bad signo");
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldact) {
|
||||
if (oldact->sa_handler == SIG_DFL) {
|
||||
// Make sure the default sig handler is executed
|
||||
// We need it to get Mozilla to dump core.
|
||||
sigaction(signo,oldact, nullptr);
|
||||
|
||||
// Now that we've restored the default handler, unmask the
|
||||
// signal and invoke it.
|
||||
|
||||
sigset_t unblock_sigs;
|
||||
sigemptyset(&unblock_sigs);
|
||||
sigaddset(&unblock_sigs, signo);
|
||||
|
||||
sigprocmask(SIG_UNBLOCK, &unblock_sigs, nullptr);
|
||||
|
||||
raise(signo);
|
||||
}
|
||||
#ifdef SA_SIGINFO
|
||||
else if (oldact->sa_sigaction &&
|
||||
(oldact->sa_flags & SA_SIGINFO) == SA_SIGINFO) {
|
||||
oldact->sa_sigaction(signo, info, context);
|
||||
}
|
||||
#endif
|
||||
else if (oldact->sa_handler && oldact->sa_handler != SIG_IGN)
|
||||
{
|
||||
oldact->sa_handler(signo);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
switch (signo) {
|
||||
case SIGQUIT:
|
||||
case SIGILL:
|
||||
case SIGABRT:
|
||||
case SIGSEGV:
|
||||
#ifndef MOZ_CRASHREPORTER
|
||||
// Retrigger the signal for those that can generate a core dump
|
||||
signal(signo, SIG_DFL);
|
||||
if (info->si_code <= 0) {
|
||||
if (syscall(__NR_tgkill, getpid(), syscall(__NR_gettid), signo) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Backstop exit call, just in case.
|
||||
_exit(signo);
|
||||
}
|
||||
|
||||
nsresult nsProfileLock::LockWithFcntl(nsIFile *aLockFile)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsAutoCString lockFilePath;
|
||||
rv = aLockFile->GetNativePath(lockFilePath);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Could not get native path");
|
||||
return rv;
|
||||
}
|
||||
|
||||
aLockFile->GetLastModifiedTime(&mReplacedLockTime);
|
||||
|
||||
mLockFileDesc = open(lockFilePath.get(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (mLockFileDesc != -1)
|
||||
{
|
||||
struct flock lock;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0; // len = 0 means entire file
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
|
||||
// If fcntl(F_GETLK) fails then the server does not support/allow fcntl(),
|
||||
// return failure rather than access denied in this case so we fallback
|
||||
// to using a symlink lock, bug 303633.
|
||||
struct flock testlock = lock;
|
||||
if (fcntl(mLockFileDesc, F_GETLK, &testlock) == -1)
|
||||
{
|
||||
close(mLockFileDesc);
|
||||
mLockFileDesc = -1;
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
else if (fcntl(mLockFileDesc, F_SETLK, &lock) == -1)
|
||||
{
|
||||
close(mLockFileDesc);
|
||||
mLockFileDesc = -1;
|
||||
|
||||
// With OS X, on NFS, errno == ENOTSUP
|
||||
// XXX Check for that and return specific rv for it?
|
||||
#ifdef DEBUG
|
||||
printf("fcntl(F_SETLK) failed. errno = %d\n", errno);
|
||||
#endif
|
||||
if (errno == EAGAIN || errno == EACCES)
|
||||
rv = NS_ERROR_FILE_ACCESS_DENIED;
|
||||
else
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_ERROR("Failed to open lock file.");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool IsSymlinkStaleLock(struct in_addr* aAddr, const char* aFileName,
|
||||
bool aHaveFcntlLock)
|
||||
{
|
||||
// the link exists; see if it's from this machine, and if
|
||||
// so if the process is still active
|
||||
char buf[1024];
|
||||
int len = readlink(aFileName, buf, sizeof buf - 1);
|
||||
if (len > 0)
|
||||
{
|
||||
buf[len] = '\0';
|
||||
char *colon = strchr(buf, ':');
|
||||
if (colon)
|
||||
{
|
||||
*colon++ = '\0';
|
||||
unsigned long addr = inet_addr(buf);
|
||||
if (addr != (unsigned long) -1)
|
||||
{
|
||||
if (colon[0] == '+' && aHaveFcntlLock) {
|
||||
// This lock was placed by a Firefox build which would have
|
||||
// taken the fnctl lock, and we've already taken the fcntl lock,
|
||||
// so the process that created this obsolete lock must be gone
|
||||
return true;
|
||||
}
|
||||
|
||||
char *after = nullptr;
|
||||
pid_t pid = strtol(colon, &after, 0);
|
||||
if (pid != 0 && *after == '\0')
|
||||
{
|
||||
if (addr != aAddr->s_addr)
|
||||
{
|
||||
// Remote lock: give up even if stuck.
|
||||
return false;
|
||||
}
|
||||
|
||||
// kill(pid,0) is a neat trick to check if a
|
||||
// process exists
|
||||
if (kill(pid, 0) == 0 || errno != ESRCH)
|
||||
{
|
||||
// Local process appears to be alive, ass-u-me it
|
||||
// is another Mozilla instance, or a compatible
|
||||
// derivative, that's currently using the profile.
|
||||
// XXX need an "are you Mozilla?" protocol
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult nsProfileLock::LockWithSymlink(nsIFile *aLockFile, bool aHaveFcntlLock)
|
||||
{
|
||||
nsresult rv;
|
||||
nsAutoCString lockFilePath;
|
||||
rv = aLockFile->GetNativePath(lockFilePath);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Could not get native path");
|
||||
return rv;
|
||||
}
|
||||
|
||||
// don't replace an existing lock time if fcntl already got one
|
||||
if (!mReplacedLockTime)
|
||||
aLockFile->GetLastModifiedTimeOfLink(&mReplacedLockTime);
|
||||
|
||||
struct in_addr inaddr;
|
||||
inaddr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
char hostname[256];
|
||||
PRStatus status = PR_GetSystemInfo(PR_SI_HOSTNAME, hostname, sizeof hostname);
|
||||
if (status == PR_SUCCESS)
|
||||
{
|
||||
char netdbbuf[PR_NETDB_BUF_SIZE];
|
||||
PRHostEnt hostent;
|
||||
status = PR_GetHostByName(hostname, netdbbuf, sizeof netdbbuf, &hostent);
|
||||
if (status == PR_SUCCESS)
|
||||
memcpy(&inaddr, hostent.h_addr, sizeof inaddr);
|
||||
}
|
||||
|
||||
char *signature =
|
||||
PR_smprintf("%s:%s%lu", inet_ntoa(inaddr), aHaveFcntlLock ? "+" : "",
|
||||
(unsigned long)getpid());
|
||||
const char *fileName = lockFilePath.get();
|
||||
int symlink_rv, symlink_errno = 0, tries = 0;
|
||||
|
||||
// use ns4.x-compatible symlinks if the FS supports them
|
||||
while ((symlink_rv = symlink(signature, fileName)) < 0)
|
||||
{
|
||||
symlink_errno = errno;
|
||||
if (symlink_errno != EEXIST)
|
||||
break;
|
||||
|
||||
if (!IsSymlinkStaleLock(&inaddr, fileName, aHaveFcntlLock))
|
||||
break;
|
||||
|
||||
// Lock seems to be bogus: try to claim it. Give up after a large
|
||||
// number of attempts (100 comes from the 4.x codebase).
|
||||
(void) unlink(fileName);
|
||||
if (++tries > 100)
|
||||
break;
|
||||
}
|
||||
|
||||
PR_smprintf_free(signature);
|
||||
signature = nullptr;
|
||||
|
||||
if (symlink_rv == 0)
|
||||
{
|
||||
// We exclusively created the symlink: record its name for eventual
|
||||
// unlock-via-unlink.
|
||||
rv = NS_OK;
|
||||
mPidLockFileName = strdup(fileName);
|
||||
if (mPidLockFileName)
|
||||
{
|
||||
PR_APPEND_LINK(this, &mPidLockList);
|
||||
if (!setupPidLockCleanup++)
|
||||
{
|
||||
// Clean up on normal termination.
|
||||
// This instanciates a dummy class, and will trigger the class
|
||||
// destructor when libxul is unloaded. This is equivalent to atexit(),
|
||||
// but gracefully handles dlclose().
|
||||
static RemovePidLockFilesExiting r;
|
||||
|
||||
// Clean up on abnormal termination, using POSIX sigaction.
|
||||
// Don't arm a handler if the signal is being ignored, e.g.,
|
||||
// because mozilla is run via nohup.
|
||||
if (!sDisableSignalHandling) {
|
||||
struct sigaction act, oldact;
|
||||
#ifdef SA_SIGINFO
|
||||
act.sa_sigaction = FatalSignalHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
#else
|
||||
act.sa_handler = FatalSignalHandler;
|
||||
#endif
|
||||
sigfillset(&act.sa_mask);
|
||||
|
||||
#define CATCH_SIGNAL(signame) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (sigaction(signame, nullptr, &oldact) == 0 && \
|
||||
oldact.sa_handler != SIG_IGN) \
|
||||
{ \
|
||||
sigaction(signame, &act, &signame##_oldact); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
CATCH_SIGNAL(SIGHUP);
|
||||
CATCH_SIGNAL(SIGINT);
|
||||
CATCH_SIGNAL(SIGQUIT);
|
||||
CATCH_SIGNAL(SIGILL);
|
||||
CATCH_SIGNAL(SIGABRT);
|
||||
CATCH_SIGNAL(SIGSEGV);
|
||||
CATCH_SIGNAL(SIGTERM);
|
||||
|
||||
#undef CATCH_SIGNAL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (symlink_errno == EEXIST)
|
||||
rv = NS_ERROR_FILE_ACCESS_DENIED;
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("symlink() failed. errno = %d\n", errno);
|
||||
#endif
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
#endif /* XP_UNIX */
|
||||
|
||||
nsresult nsProfileLock::GetReplacedLockTime(PRTime *aResult) {
|
||||
*aResult = mReplacedLockTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsProfileLock::Lock(nsIFile* aProfileDir,
|
||||
nsIProfileUnlocker* *aUnlocker)
|
||||
{
|
||||
#if defined (XP_MACOSX)
|
||||
NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
|
||||
NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "parent.lock");
|
||||
#elif defined (XP_UNIX)
|
||||
NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "lock");
|
||||
NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
|
||||
#else
|
||||
NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, "parent.lock");
|
||||
#endif
|
||||
|
||||
nsresult rv;
|
||||
if (aUnlocker)
|
||||
*aUnlocker = nullptr;
|
||||
|
||||
NS_ENSURE_STATE(!mHaveLock);
|
||||
|
||||
bool isDir;
|
||||
rv = aProfileDir->IsDirectory(&isDir);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
if (!isDir)
|
||||
return NS_ERROR_FILE_NOT_DIRECTORY;
|
||||
|
||||
nsCOMPtr<nsIFile> lockFile;
|
||||
rv = aProfileDir->Clone(getter_AddRefs(lockFile));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = lockFile->Append(LOCKFILE_NAME);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
// First, try locking using fcntl. It is more reliable on
|
||||
// a local machine, but may not be supported by an NFS server.
|
||||
|
||||
rv = LockWithFcntl(lockFile);
|
||||
if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED))
|
||||
{
|
||||
// If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
|
||||
// assume we tried an NFS that does not support it. Now, try with symlink.
|
||||
rv = LockWithSymlink(lockFile, false);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
// Check for the old-style lock used by pre-mozilla 1.3 builds.
|
||||
// Those builds used an earlier check to prevent the application
|
||||
// from launching if another instance was already running. Because
|
||||
// of that, we don't need to create an old-style lock as well.
|
||||
struct LockProcessInfo
|
||||
{
|
||||
ProcessSerialNumber psn;
|
||||
unsigned long launchDate;
|
||||
};
|
||||
|
||||
PRFileDesc *fd = nullptr;
|
||||
int32_t ioBytes;
|
||||
ProcessInfoRec processInfo;
|
||||
LockProcessInfo lockProcessInfo;
|
||||
|
||||
rv = lockFile->SetLeafName(OLD_LOCKFILE_NAME);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = lockFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
ioBytes = PR_Read(fd, &lockProcessInfo, sizeof(LockProcessInfo));
|
||||
PR_Close(fd);
|
||||
|
||||
if (ioBytes == sizeof(LockProcessInfo))
|
||||
{
|
||||
#ifdef __LP64__
|
||||
processInfo.processAppRef = nullptr;
|
||||
#else
|
||||
processInfo.processAppSpec = nullptr;
|
||||
#endif
|
||||
processInfo.processName = nullptr;
|
||||
processInfo.processInfoLength = sizeof(ProcessInfoRec);
|
||||
if (::GetProcessInformation(&lockProcessInfo.psn, &processInfo) == noErr &&
|
||||
processInfo.processLaunchDate == lockProcessInfo.launchDate)
|
||||
{
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_WARNING("Could not read lock file - ignoring lock");
|
||||
}
|
||||
}
|
||||
rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc.
|
||||
}
|
||||
#elif defined(XP_UNIX)
|
||||
// Get the old lockfile name
|
||||
nsCOMPtr<nsIFile> oldLockFile;
|
||||
rv = aProfileDir->Clone(getter_AddRefs(oldLockFile));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = oldLockFile->Append(OLD_LOCKFILE_NAME);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// First, try locking using fcntl. It is more reliable on
|
||||
// a local machine, but may not be supported by an NFS server.
|
||||
rv = LockWithFcntl(lockFile);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Check to see whether there is a symlink lock held by an older
|
||||
// Firefox build, and also place our own symlink lock --- but
|
||||
// mark it "obsolete" so that other newer builds can break the lock
|
||||
// if they obtain the fcntl lock
|
||||
rv = LockWithSymlink(oldLockFile, true);
|
||||
|
||||
// If the symlink failed for some reason other than it already
|
||||
// exists, then something went wrong e.g. the file system
|
||||
// doesn't support symlinks, or we don't have permission to
|
||||
// create a symlink there. In such cases we should just
|
||||
// continue because it's unlikely there is an old build
|
||||
// running with a symlink there and we've already successfully
|
||||
// placed a fcntl lock.
|
||||
if (rv != NS_ERROR_FILE_ACCESS_DENIED)
|
||||
rv = NS_OK;
|
||||
}
|
||||
else if (rv != NS_ERROR_FILE_ACCESS_DENIED)
|
||||
{
|
||||
// If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
|
||||
// assume we tried an NFS that does not support it. Now, try with symlink
|
||||
// using the old symlink path
|
||||
rv = LockWithSymlink(oldLockFile, false);
|
||||
}
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
nsAutoString filePath;
|
||||
rv = lockFile->GetPath(filePath);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
lockFile->GetLastModifiedTime(&mReplacedLockTime);
|
||||
|
||||
// always create the profile lock and never delete it so we can use its
|
||||
// modification timestamp to detect startup crashes
|
||||
mLockFileHandle = CreateFileW(filePath.get(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, // no sharing - of course
|
||||
nullptr,
|
||||
CREATE_ALWAYS,
|
||||
0,
|
||||
nullptr);
|
||||
if (mLockFileHandle == INVALID_HANDLE_VALUE) {
|
||||
if (aUnlocker) {
|
||||
RefPtr<mozilla::ProfileUnlockerWin> unlocker(
|
||||
new mozilla::ProfileUnlockerWin(filePath));
|
||||
if (NS_SUCCEEDED(unlocker->Init())) {
|
||||
nsCOMPtr<nsIProfileUnlocker> unlockerInterface(
|
||||
do_QueryObject(unlocker));
|
||||
unlockerInterface.forget(aUnlocker);
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mHaveLock = true;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsProfileLock::Unlock(bool aFatalSignal)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mHaveLock)
|
||||
{
|
||||
#if defined (XP_WIN)
|
||||
if (mLockFileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(mLockFileHandle);
|
||||
mLockFileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#elif defined (XP_UNIX)
|
||||
if (mPidLockFileName)
|
||||
{
|
||||
PR_REMOVE_LINK(this);
|
||||
(void) unlink(mPidLockFileName);
|
||||
|
||||
// Only free mPidLockFileName if we're not in the fatal signal
|
||||
// handler. The problem is that a call to free() might be the
|
||||
// cause of this fatal signal. If so, calling free() might cause
|
||||
// us to wait on the malloc implementation's lock. We're already
|
||||
// holding this lock, so we'll deadlock. See bug 522332.
|
||||
if (!aFatalSignal)
|
||||
free(mPidLockFileName);
|
||||
mPidLockFileName = nullptr;
|
||||
}
|
||||
if (mLockFileDesc != -1)
|
||||
{
|
||||
close(mLockFileDesc);
|
||||
mLockFileDesc = -1;
|
||||
// Don't remove it
|
||||
}
|
||||
#endif
|
||||
|
||||
mHaveLock = false;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
95
toolkit/profile/nsProfileLock.h
Normal file
95
toolkit/profile/nsProfileLock.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 __nsProfileLock_h___
|
||||
#define __nsProfileLock_h___
|
||||
|
||||
#include "nsIFile.h"
|
||||
|
||||
class nsIProfileUnlocker;
|
||||
|
||||
#if defined (XP_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined (XP_UNIX)
|
||||
#include <signal.h>
|
||||
#include "prclist.h"
|
||||
#endif
|
||||
|
||||
class nsProfileLock
|
||||
#if defined (XP_UNIX)
|
||||
: public PRCList
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
nsProfileLock();
|
||||
nsProfileLock(nsProfileLock& src);
|
||||
|
||||
~nsProfileLock();
|
||||
|
||||
nsProfileLock& operator=(nsProfileLock& rhs);
|
||||
|
||||
/**
|
||||
* Attempt to lock a profile directory.
|
||||
*
|
||||
* @param aProfileDir [in] The profile directory to lock.
|
||||
* @param aUnlocker [out] Optional. This is only returned when locking
|
||||
* fails with NS_ERROR_FILE_ACCESS_DENIED, and may not
|
||||
* be returned at all.
|
||||
* @throws NS_ERROR_FILE_ACCESS_DENIED if the profile is locked.
|
||||
*/
|
||||
nsresult Lock(nsIFile* aProfileDir, nsIProfileUnlocker* *aUnlocker);
|
||||
|
||||
/**
|
||||
* Unlock a profile directory. If you're unlocking the directory because
|
||||
* the application is in the process of shutting down because of a fatal
|
||||
* signal, set aFatalSignal to true.
|
||||
*/
|
||||
nsresult Unlock(bool aFatalSignal = false);
|
||||
|
||||
/**
|
||||
* Get the modification time of a replaced profile lock, otherwise 0.
|
||||
*/
|
||||
nsresult GetReplacedLockTime(PRTime* aResult);
|
||||
|
||||
private:
|
||||
bool mHaveLock;
|
||||
PRTime mReplacedLockTime;
|
||||
|
||||
#if defined (XP_WIN)
|
||||
HANDLE mLockFileHandle;
|
||||
#elif defined (XP_UNIX)
|
||||
|
||||
struct RemovePidLockFilesExiting {
|
||||
RemovePidLockFilesExiting() {}
|
||||
~RemovePidLockFilesExiting() {
|
||||
RemovePidLockFiles(false);
|
||||
}
|
||||
};
|
||||
|
||||
static void RemovePidLockFiles(bool aFatalSignal);
|
||||
static void FatalSignalHandler(int signo
|
||||
#ifdef SA_SIGINFO
|
||||
, siginfo_t *info, void *context
|
||||
#endif
|
||||
);
|
||||
static PRCList mPidLockList;
|
||||
|
||||
nsresult LockWithFcntl(nsIFile *aLockFile);
|
||||
|
||||
/**
|
||||
* @param aHaveFcntlLock if true, we've already acquired an fcntl lock so this
|
||||
* lock is merely an "obsolete" lock to keep out old Firefoxes
|
||||
*/
|
||||
nsresult LockWithSymlink(nsIFile *aLockFile, bool aHaveFcntlLock);
|
||||
|
||||
char* mPidLockFileName;
|
||||
int mLockFileDesc;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif /* __nsProfileLock_h___ */
|
||||
32
toolkit/profile/nsProfileStringTypes.h
Normal file
32
toolkit/profile/nsProfileStringTypes.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* We support two builds of the directory service provider.
|
||||
* One, linked into the profile component, uses the internal
|
||||
* string API. The other can be used by standalone embedding
|
||||
* clients, and uses embed strings.
|
||||
* To keep the code clean, we are using typedefs to equate
|
||||
* embed/internal string types. We are also defining some
|
||||
* internal macros in terms of the embedding strings API.
|
||||
*
|
||||
* When modifying the profile directory service provider, be
|
||||
* sure to use methods supported by both the internal and
|
||||
* embed strings APIs.
|
||||
*/
|
||||
|
||||
#ifndef MOZILLA_INTERNAL_API
|
||||
|
||||
#include "nsEmbedString.h"
|
||||
|
||||
typedef nsCString nsPromiseFlatCString;
|
||||
typedef nsCString nsAutoCString;
|
||||
|
||||
#define PromiseFlatCString nsCString
|
||||
|
||||
#else
|
||||
#include "nsString.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
#endif
|
||||
1117
toolkit/profile/nsToolkitProfileService.cpp
Normal file
1117
toolkit/profile/nsToolkitProfileService.cpp
Normal file
File diff suppressed because it is too large
Load diff
7
toolkit/profile/test/.eslintrc.js
Normal file
7
toolkit/profile/test/.eslintrc.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../../../testing/mochitest/chrome.eslintrc.js"
|
||||
]
|
||||
};
|
||||
3
toolkit/profile/test/chrome.ini
Normal file
3
toolkit/profile/test/chrome.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_create_profile.xul]
|
||||
134
toolkit/profile/test/test_create_profile.xul
Normal file
134
toolkit/profile/test/test_create_profile.xul
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=543854
|
||||
-->
|
||||
<window title="Mozilla Bug 543854"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=543854"
|
||||
target="_blank">Mozilla Bug 543854</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 543854 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const ASCIIName = "myprofile";
|
||||
const UnicodeName = "\u09A0\u09BE\u0995\u09C1\u09B0"; // A Bengali name
|
||||
|
||||
var gDirService;
|
||||
var gIOService;
|
||||
var gProfileService;
|
||||
|
||||
var gDefaultLocalProfileParent;
|
||||
|
||||
gDirService = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
|
||||
gIOService = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
|
||||
gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
||||
getService(Ci.nsIToolkitProfileService);
|
||||
|
||||
gDefaultLocalProfileParent = gDirService.get("DefProfLRt", Ci.nsIFile);
|
||||
|
||||
createProfile(ASCIIName);
|
||||
createProfile(UnicodeName);
|
||||
SimpleTest.finish();
|
||||
|
||||
/**
|
||||
* Read the contents of an nsIFile. Throws on error.
|
||||
|
||||
* @param file an nsIFile instance.
|
||||
* @return string contents.
|
||||
*/
|
||||
function readFile(file) {
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
let sstream = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
|
||||
const RO = 0x01;
|
||||
const READ_OTHERS = 4;
|
||||
|
||||
fstream.init(file, RO, READ_OTHERS, 0);
|
||||
sstream.init(fstream);
|
||||
let out = sstream.read(sstream.available());
|
||||
sstream.close();
|
||||
fstream.close();
|
||||
return out;
|
||||
}
|
||||
|
||||
function checkBounds(lowerBound, value, upperBound) {
|
||||
ok(lowerBound <= value, "value " + value +
|
||||
" is above lower bound " + lowerBound);
|
||||
ok(upperBound >= value, "value " + value +
|
||||
" is within upper bound " + upperBound);
|
||||
}
|
||||
|
||||
function createProfile(profileName) {
|
||||
// Filesystem precision is lower than Date precision.
|
||||
let lowerBound = Date.now() - 1000;
|
||||
|
||||
let profile = gProfileService.createProfile(null, profileName);
|
||||
|
||||
// check that the directory was created
|
||||
isnot(profile, null, "Profile " + profileName + " created");
|
||||
|
||||
let profileDir = profile.rootDir;
|
||||
|
||||
ok(profileDir.exists(), "Profile dir created");
|
||||
ok(profileDir.isDirectory(), "Profile dir is a directory");
|
||||
|
||||
let profileDirPath = profileDir.path;
|
||||
|
||||
is(profileDirPath.substr(profileDirPath.length - profileName.length),
|
||||
profileName, "Profile dir has expected name");
|
||||
|
||||
// Ensure that our timestamp file was created.
|
||||
let jsonFile = profileDir.clone();
|
||||
jsonFile.append("times.json");
|
||||
ok(jsonFile.path, "Path is " + jsonFile.path);
|
||||
ok(jsonFile.exists(), "Times file was created");
|
||||
ok(jsonFile.isFile(), "Times file is a file");
|
||||
let json = JSON.parse(readFile(jsonFile));
|
||||
|
||||
let upperBound = Date.now() + 1000;
|
||||
|
||||
let created = json.created;
|
||||
ok(created, "created is set");
|
||||
|
||||
// Check against real clock time.
|
||||
checkBounds(lowerBound, created, upperBound);
|
||||
|
||||
// Clean up the profile before local profile test.
|
||||
profile.remove(true);
|
||||
|
||||
// Create with non-null aRootDir
|
||||
profile = gProfileService.createProfile(profileDir, profileName);
|
||||
|
||||
let localProfileDir = profile.localDir;
|
||||
ok(gDefaultLocalProfileParent.contains(localProfileDir, false),
|
||||
"Local profile dir created in DefProfLRt");
|
||||
|
||||
// Clean up the profile.
|
||||
profile.remove(true);
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
||||
Loading…
Add table
Add a link
Reference in a new issue