summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/profile
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/components/profile')
-rw-r--r--comm/suite/components/profile/content/profileSelection.js344
-rw-r--r--comm/suite/components/profile/content/profileSelection.xul85
-rw-r--r--comm/suite/components/profile/jar.mn8
-rw-r--r--comm/suite/components/profile/moz.build13
-rwxr-xr-xcomm/suite/components/profile/nsSuiteDirectoryProvider.cpp248
-rw-r--r--comm/suite/components/profile/nsSuiteDirectoryProvider.h58
6 files changed, 756 insertions, 0 deletions
diff --git a/comm/suite/components/profile/content/profileSelection.js b/comm/suite/components/profile/content/profileSelection.js
new file mode 100644
index 0000000000..6400d73359
--- /dev/null
+++ b/comm/suite/components/profile/content/profileSelection.js
@@ -0,0 +1,344 @@
+/* -*- 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gProfileBundle;
+var gBrandBundle;
+var gProfileService;
+var gProfileManagerMode = "selection";
+var gDialogParams = window.arguments[0]
+ .QueryInterface(Ci.nsIDialogParamBlock);
+
+function StartUp()
+{
+ gProfileBundle = document.getElementById("bundle_profile");
+ gBrandBundle = document.getElementById("bundle_brand");
+ if (gDialogParams.objects) {
+ document.documentElement.getButton("accept").setAttribute("label",
+ document.documentElement.getAttribute("buttonlabelstart"));
+ document.documentElement.getButton("cancel").setAttribute("label",
+ document.documentElement.getAttribute("buttonlabelexit"));
+ document.getElementById('intro').textContent =
+ document.getElementById('intro').getAttribute("start");
+ document.getElementById('offlineState').hidden = false;
+ gDialogParams.SetInt(0, 0);
+ }
+
+ gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"]
+ .getService(Ci.nsIToolkitProfileService);
+ var profileEnum = gProfileService.profiles;
+ var selectedProfile = null;
+ try {
+ selectedProfile = gProfileService.selectedProfile;
+ }
+ catch (ex) {
+ }
+ while (profileEnum.hasMoreElements()) {
+ AddItem(profileEnum.getNext().QueryInterface(Ci.nsIToolkitProfile),
+ selectedProfile);
+ }
+
+ var autoSelect = document.getElementById("autoSelect");
+ if (Services.prefs.getBoolPref("profile.manage_only_at_launch"))
+ autoSelect.hidden = true;
+ else
+ autoSelect.checked = gProfileService.startWithLastProfile;
+
+ DoEnabling();
+}
+
+// function : <profileSelection.js>::AddItem();
+// purpose : utility function for adding items to a tree.
+function AddItem(aProfile, aProfileToSelect)
+{
+ var tree = document.getElementById("profiles");
+ var treeitem = document.createElement("treeitem");
+ var treerow = document.createElement("treerow");
+ var treecell = document.createElement("treecell");
+ var treetip = document.getElementById("treetip");
+ var profileDir = gProfileService.getProfileByName(aProfile.name).rootDir;
+
+ treecell.setAttribute("label", aProfile.name);
+ treerow.appendChild(treecell);
+ treeitem.appendChild(treerow);
+ treeitem.setAttribute("tooltip", profileDir.path);
+ treetip.setAttribute("value", profileDir.path);
+ tree.lastChild.appendChild(treeitem);
+ treeitem.profile = aProfile;
+ if (aProfile == aProfileToSelect) {
+ var profileIndex = tree.view.getIndexOfItem(treeitem);
+ tree.view.selection.select(profileIndex);
+ tree.treeBoxObject.ensureRowIsVisible(profileIndex);
+ }
+}
+
+// function : <profileSelection.js>::AcceptDialog();
+// purpose : sets the current profile to the selected profile (user choice: "Start Mozilla")
+function AcceptDialog()
+{
+ var autoSelect = document.getElementById("autoSelect");
+ if (!autoSelect.hidden) {
+ gProfileService.startWithLastProfile = autoSelect.checked;
+ gProfileService.flush();
+ }
+
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+
+ if (!gDialogParams.objects) {
+ var profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ var profLD = Services.dirsvc.get("ProfLD", Ci.nsIFile);
+
+ if (selected.profile.rootDir.equals(profD) &&
+ selected.profile.localDir.equals(profLD))
+ return true;
+ }
+
+ try {
+ var profileLock = selected.profile.lock({});
+ gProfileService.selectedProfile = selected.profile;
+ gProfileService.defaultProfile = selected.profile;
+ gProfileService.flush();
+ if (gDialogParams.objects) {
+ gDialogParams.objects.insertElementAt(profileLock, 0);
+ gProfileService.startOffline = document.getElementById("offlineState").checked;
+ gDialogParams.SetInt(0, 1);
+ gDialogParams.SetString(0, selected.profile.name);
+ return true;
+ }
+ profileLock.unlock();
+ } catch (e) {
+ var brandName = gBrandBundle.getString("brandShortName");
+ var message = gProfileBundle.getFormattedString("dirLocked",
+ [brandName, selected.profile.name]);
+ Services.prompt.alert(window, null, message);
+ return false;
+ }
+
+ // Although switching profile works by performing a restart internally,
+ // the user is quitting the old profile, so make it look like a quit.
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
+
+ if (cancelQuit.data) {
+ return false;
+ }
+
+ try {
+ var env = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+ env.set("XRE_PROFILE_NAME", selected.profile.name);
+ env.set("XRE_PROFILE_PATH", selected.profile.rootDir.path);
+ env.set("XRE_PROFILE_LOCAL_PATH", selected.profile.localDir.path);
+ var app = Services.startup;
+ app.quit(app.eAttemptQuit | app.eRestart);
+ return true;
+ }
+ catch (e) {
+ env.set("XRE_PROFILE_NAME", "");
+ env.set("XRE_PROFILE_PATH", "");
+ env.set("XRE_PROFILE_LOCAL_PATH", "");
+ return false;
+ }
+}
+
+// invoke the createProfile Wizard
+function CreateProfileWizard()
+{
+ window.openDialog('chrome://mozapps/content/profile/createProfileWizard.xul',
+ '', 'centerscreen,chrome,modal,titlebar');
+}
+
+// update the display to show the additional profile
+function CreateProfile(aProfile)
+{
+ gProfileService.flush();
+ AddItem(aProfile, aProfile);
+}
+
+// rename the selected profile
+function RenameProfile()
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ var profileName = selected.profile.name;
+ var newName = {value: profileName};
+ var dialogTitle = gProfileBundle.getString("renameProfileTitle");
+ var msg = gProfileBundle.getFormattedString("renameProfilePrompt", [profileName]);
+ var ps = Services.prompt;
+ if (ps.prompt(window, dialogTitle, msg, newName, null, {value: 0}) &&
+ newName.value != profileName) {
+ if (!/\S/.test(newName.value)) {
+ ps.alert(window, gProfileBundle.getString("profileNameInvalidTitle"),
+ gProfileBundle.getString("profileNameEmpty"));
+ return false;
+ }
+
+ if (/([\\*:?<>|\/\"])/.test(newName.value)) {
+ ps.alert(window, gProfileBundle.getString("profileNameInvalidTitle"),
+ gProfileBundle.getFormattedString("invalidChar", [RegExp.$1]));
+ return false;
+ }
+
+ try {
+ gProfileService.getProfileByName(newName.value);
+ ps.alert(window, gProfileBundle.getString("profileExistsTitle"),
+ gProfileBundle.getString("profileExists"));
+ return false;
+ }
+ catch (e) {
+ }
+
+ selected.profile.name = newName.value;
+ gProfileService.flush();
+ selected.firstChild.firstChild.setAttribute("label", newName.value);
+ }
+}
+
+function ConfirmDelete()
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ if (!selected.profile.rootDir.exists()) {
+ DeleteProfile(false);
+ return;
+ }
+
+ try {
+ var profileLock = selected.profile.lock({});
+ var dialogTitle = gProfileBundle.getString("deleteTitle");
+ var dialogText;
+
+ var path = selected.profile.rootDir.path;
+ dialogText = gProfileBundle.getFormattedString("deleteProfile", [path]);
+ var ps = Services.prompt;
+ var buttonPressed = ps.confirmEx(window, dialogTitle, dialogText,
+ (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1) +
+ (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_2),
+ gProfileBundle.getString("dontDeleteFiles"), null,
+ gProfileBundle.getString("deleteFiles"), null, {value: 0});
+ profileLock.unlock();
+ if (buttonPressed != 1)
+ DeleteProfile(buttonPressed == 2);
+ } catch (e) {
+ var dialogTitle = gProfileBundle.getString("deleteTitle");
+ var brandName = gBrandBundle.getString("brandShortName");
+ var dialogText = gProfileBundle.getFormattedString("deleteLocked",
+ [brandName, selected.profile.name]);
+ ps.alert(window, dialogTitle, dialogText);
+ }
+}
+
+// Delete the profile, with the delete flag set as per instruction above.
+function DeleteProfile(aDeleteFiles)
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ var previous = profileTree.currentIndex && profileTree.currentIndex - 1;
+
+ try {
+ selected.profile.remove(aDeleteFiles);
+ gProfileService.flush();
+ selected.remove();
+
+ if (profileTree.view.rowCount != 0) {
+ profileTree.view.selection.select(previous);
+ profileTree.treeBoxObject.ensureRowIsVisible(previous);
+ }
+
+ // set the button state
+ DoEnabling();
+ }
+ catch (ex) {
+ dump("Exception during profile deletion.\n");
+ }
+}
+
+function SwitchProfileManagerMode()
+{
+ var captionLine;
+ var prattleIndex;
+
+ if (gProfileManagerMode == "selection") {
+ prattleIndex = 1;
+ captionLine = gProfileBundle.getString("manageTitle");
+
+ document.getElementById("profiles").focus();
+
+ // hide the manage profiles button...
+ document.documentElement.getButton("extra2").hidden = true;
+ gProfileManagerMode = "manager";
+ }
+ else {
+ prattleIndex = 0;
+ captionLine = gProfileBundle.getString("selectTitle");
+ gProfileManagerMode = "selection";
+ }
+
+ // swap deck
+ document.getElementById("prattle").selectedIndex = prattleIndex;
+
+ // change the title of the profile manager/selection window.
+ document.getElementById("header").setAttribute("description", captionLine);
+ document.title = captionLine;
+}
+
+// do button enabling based on tree selection
+function DoEnabling()
+{
+ var acceptButton = document.documentElement.getButton("accept");
+ var deleteButton = document.getElementById("deleteButton");
+ var renameButton = document.getElementById("renameButton");
+
+ var disabled = document.getElementById("profiles").view.selection.count == 0;
+ acceptButton.disabled = disabled;
+ deleteButton.disabled = disabled;
+ renameButton.disabled = disabled;
+}
+
+// handle key event on tree
+function HandleKeyEvent(aEvent)
+{
+ if (gProfileManagerMode != "manager")
+ return;
+
+ switch (aEvent.keyCode)
+ {
+ case KeyEvent.DOM_VK_BACK_SPACE:
+ case KeyEvent.DOM_VK_DELETE:
+ if (!document.getElementById("deleteButton").disabled)
+ ConfirmDelete();
+ break;
+ case KeyEvent.DOM_VK_F2:
+ if (!document.getElementById("renameButton").disabled)
+ RenameProfile();
+ }
+}
+
+function HandleClickEvent(aEvent)
+{
+ if (aEvent.button == 0 && aEvent.target.parentNode.view.selection.count != 0 && AcceptDialog()) {
+ window.close();
+ return true;
+ }
+
+ return false;
+}
+
+function HandleToolTipEvent(aEvent)
+{
+ var treeTip = document.getElementById("treetip");
+ var tree = document.getElementById("profiles");
+
+ var cell = tree.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.row < 0)
+ aEvent.preventDefault();
+ else
+ treeTip.label = tree.view.getItemAtIndex(cell.row).tooltip;
+}
diff --git a/comm/suite/components/profile/content/profileSelection.xul b/comm/suite/components/profile/content/profileSelection.xul
new file mode 100644
index 0000000000..dd62b4a7dd
--- /dev/null
+++ b/comm/suite/components/profile/content/profileSelection.xul
@@ -0,0 +1,85 @@
+<?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://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/profile/profile.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % profileDTD SYSTEM "chrome://communicator/locale/profile/profileSelection.dtd">
+%profileDTD;
+]>
+
+<dialog id="profileWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&windowTitle.label;"
+ windowtype="mozilla:profileSelection"
+ orient="vertical"
+ style="width: 42em;"
+ buttons="accept,cancel,extra2"
+ buttonlabelaccept="&select.label;"
+ buttonlabelstart="&start.label;"
+ buttonlabelexit="&exit.label;"
+ buttonlabelextra2="&manage.label;"
+ buttonaccesskeyextra2="&manage.accesskey;"
+ ondialogaccept="return AcceptDialog();"
+ ondialogextra2="SwitchProfileManagerMode();"
+ onload="StartUp();">
+
+ <stringbundle id="bundle_profile"
+ src="chrome://communicator/locale/profile/profileSelection.properties"/>
+ <stringbundle id="bundle_brand"
+ src="chrome://branding/locale/brand.properties"/>
+
+ <script src="chrome://communicator/content/profile/profileSelection.js"/>
+ <script src="chrome://mozapps/content/profile/createProfileWizard.js"/>
+
+ <dialogheader id="header" title="&profileManager.title;" description="&windowTitle.label;"/>
+
+ <hbox class="wizard-box" flex="1">
+
+ <!-- instructions -->
+ <deck id="prattle">
+ <description id="intro" start="&introStart.label;">&introSwitch.label;</description>
+ <vbox>
+ <description id="label">&profileManagerText.label;</description>
+ <separator/>
+ <hbox>
+ <vbox flex="1" id="managebuttons">
+ <button id="newButton" label="&newButton.label;" accesskey="&newButton.accesskey;" oncommand="CreateProfileWizard();"/>
+ <button id="renameButton" label="&renameButton.label;" accesskey="&renameButton.accesskey;" oncommand="RenameProfile();"/>
+ <button id="deleteButton" label="&deleteButton.label;" accesskey="&deleteButton.accesskey;" oncommand="ConfirmDelete();"/>
+ </vbox>
+ <spacer flex="2"/>
+ </hbox>
+ </vbox>
+ </deck>
+
+ <separator class="thin" orient="vertical"/>
+
+ <vbox flex="1">
+ <tooltip id="treetip"
+ onpopupshowing="HandleToolTipEvent(event);">
+ </tooltip>
+ <tree id="profiles" flex="1" seltype="single"
+ hidecolumnpicker="true"
+ onselect="DoEnabling();"
+ onkeypress="HandleKeyEvent(event);">
+ <treecols>
+ <treecol label="&availableProfiles.label;" flex="1" sortLocked="true"/>
+ </treecols>
+ <treechildren tooltip="treetip"
+ ondblclick="HandleClickEvent(event);"/>
+ </tree>
+ <checkbox id="offlineState" label="&offlineState.label;" accesskey="&offlineState.accesskey;" hidden="true"/>
+ <checkbox id="autoSelect" label="&autoSelect.label;" accesskey="&autoSelect.accesskey;"/>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/components/profile/jar.mn b/comm/suite/components/profile/jar.mn
new file mode 100644
index 0000000000..bf9706ba55
--- /dev/null
+++ b/comm/suite/components/profile/jar.mn
@@ -0,0 +1,8 @@
+# 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/.
+
+comm.jar:
+% override chrome://mozapps/content/profile/profileSelection.xul chrome://communicator/content/profile/profileSelection.xul
+ content/communicator/profile/profileSelection.js (content/profileSelection.js)
+ content/communicator/profile/profileSelection.xul (content/profileSelection.xul)
diff --git a/comm/suite/components/profile/moz.build b/comm/suite/components/profile/moz.build
new file mode 100644
index 0000000000..0f2d51ba64
--- /dev/null
+++ b/comm/suite/components/profile/moz.build
@@ -0,0 +1,13 @@
+# -*- 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/.
+
+SOURCES += [
+ "nsSuiteDirectoryProvider.cpp",
+]
+
+FINAL_LIBRARY = "suite"
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp b/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp
new file mode 100755
index 0000000000..9d19615625
--- /dev/null
+++ b/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp
@@ -0,0 +1,248 @@
+/* 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 "nsSuiteDirectoryProvider.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIPrefBranch.h"
+#include "nsDirectoryServiceDefs.h"
+#include "mozilla/intl/LocaleService.h"
+#include "nsIPrefService.h"
+#include "nsArrayEnumerator.h"
+#include "nsEnumeratorUtils.h"
+
+using mozilla::intl::LocaleService;
+
+NS_IMPL_ISUPPORTS(nsSuiteDirectoryProvider,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2)
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::GetFile(const char *aKey,
+ bool *aPersist,
+ nsIFile* *aResult)
+{
+ // NOTE: This function can be reentrant through the NS_GetSpecialDirectory
+ // call, so be careful not to cause infinite recursion.
+ // i.e. the check for supported files must come first.
+ const char* leafName = nullptr;
+
+ if (!strcmp(aKey, NS_APP_BOOKMARKS_50_FILE))
+ leafName = "bookmarks.html";
+ else if (!strcmp(aKey, NS_APP_USER_PANELS_50_FILE))
+ leafName = "panels.rdf";
+ else
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> parentDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(parentDir));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIFile> file;
+ rv = parentDir->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDependentCString leafStr(leafName);
+ file->AppendNative(leafStr);
+
+ bool exists;
+ if (NS_SUCCEEDED(file->Exists(&exists)) && !exists)
+ EnsureProfileFile(leafStr, parentDir, file);
+
+ *aPersist = true;
+ NS_IF_ADDREF(*aResult = file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::GetFiles(const char *aKey,
+ nsISimpleEnumerator* *aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMArray<nsIFile> baseFiles;
+ AppendDistroSearchDirs(dirSvc, baseFiles);
+
+ nsCOMPtr<nsISimpleEnumerator> baseEnum;
+ rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_ERROR_FAILURE;
+}
+
+void
+nsSuiteDirectoryProvider::EnsureProfileFile(const nsACString& aLeafName,
+ nsIFile* aParentDir,
+ nsIFile* aTarget)
+{
+ nsCOMPtr<nsIFile> defaultsDir;
+
+ NS_GetSpecialDirectory(NS_APP_DEFAULTS_50_DIR,
+ getter_AddRefs(defaultsDir));
+ if (!defaultsDir)
+ return;
+
+ nsresult rv = defaultsDir->AppendNative("profile"_ns);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ defaultsDir->AppendNative(aLeafName);
+
+ defaultsDir->CopyToNative(aParentDir, aLeafName);
+}
+
+NS_IMPL_ISUPPORTS(nsSuiteDirectoryProvider::AppendingEnumerator,
+ nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::AppendingEnumerator::HasMoreElements(bool *aResult)
+{
+ *aResult = mNext != nullptr;
+ return NS_OK;
+}
+
+void
+nsSuiteDirectoryProvider::AppendingEnumerator::GetNext()
+{
+ // Ignore all errors
+
+ bool more;
+ while (NS_SUCCEEDED(mBase->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> nextSupports;
+ mBase->GetNext(getter_AddRefs(nextSupports));
+
+ mNext = do_QueryInterface(nextSupports);
+ if (!mNext)
+ continue;
+
+ mNext->AppendNative(mLeafName);
+
+ bool exists;
+ if (NS_SUCCEEDED(mNext->Exists(&exists)) && exists)
+ return;
+ }
+
+ mNext = nullptr;
+}
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::AppendingEnumerator::GetNext(nsISupports* *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!mNext) {
+ *aResult = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ADDREF(*aResult = mNext);
+
+ GetNext();
+
+ return NS_OK;
+}
+
+nsSuiteDirectoryProvider::AppendingEnumerator::AppendingEnumerator
+ (nsISimpleEnumerator* aBase, const char* const aLeafName) :
+ mBase(aBase), mLeafName(aLeafName)
+{
+ // Initialize mNext to begin.
+ GetNext();
+}
+
+// Appends the distribution-specific search engine directories to the
+// array. The directory structure is as follows:
+
+// appdir/
+// \- distribution/
+// \- searchplugins/
+// |- common/
+// \- locale/
+// |- <locale 1>/
+// ...
+// \- <locale N>/
+
+// common engines are loaded for all locales. If there is no locale
+// directory for the current locale, there is a pref:
+// "distribution.searchplugins.defaultLocale"
+// which specifies a default locale to use.
+
+void
+nsSuiteDirectoryProvider::AppendDistroSearchDirs(nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array)
+{
+ nsCOMPtr<nsIFile> searchPlugins;
+ nsresult rv = aDirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(searchPlugins));
+ if (NS_FAILED(rv))
+ return;
+ searchPlugins->AppendNative("distribution"_ns);
+ searchPlugins->AppendNative("searchplugins"_ns);
+
+ bool exists;
+ rv = searchPlugins->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ return;
+
+ nsCOMPtr<nsIFile> commonPlugins;
+ rv = searchPlugins->Clone(getter_AddRefs(commonPlugins));
+ if (NS_SUCCEEDED(rv)) {
+ commonPlugins->AppendNative("common"_ns);
+ rv = commonPlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists)
+ array.AppendObject(commonPlugins);
+ }
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs) {
+ nsCOMPtr<nsIFile> localePlugins;
+ rv = searchPlugins->Clone(getter_AddRefs(localePlugins));
+ if (NS_FAILED(rv))
+ return;
+
+ localePlugins->AppendNative("locale"_ns);
+
+ // we didn't append the locale dir - try the default one
+ nsCString defLocale;
+ rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
+ defLocale);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> defLocalePlugins;
+ rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
+ if (NS_SUCCEEDED(rv)) {
+ defLocalePlugins->AppendNative(defLocale);
+ rv = defLocalePlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists) {
+ array.AppendObject(defLocalePlugins);
+ return; // all done
+ }
+ }
+ }
+
+ // we didn't have a defaultLocale, use the user agent locale
+ nsAutoCString locale;
+ LocaleService::GetInstance()->GetAppLocaleAsLangTag(locale);
+
+ nsCOMPtr<nsIFile> curLocalePlugins;
+ rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins));
+ if (NS_SUCCEEDED(rv)) {
+ curLocalePlugins->AppendNative(locale);
+ rv = curLocalePlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists) {
+ array.AppendObject(curLocalePlugins);
+ return; // all done
+ }
+ }
+ }
+}
diff --git a/comm/suite/components/profile/nsSuiteDirectoryProvider.h b/comm/suite/components/profile/nsSuiteDirectoryProvider.h
new file mode 100644
index 0000000000..6a06be3c30
--- /dev/null
+++ b/comm/suite/components/profile/nsSuiteDirectoryProvider.h
@@ -0,0 +1,58 @@
+/* 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 SuiteDirectoryProvider_h__
+#define SuiteDirectoryProvider_h__
+
+#include "nsCOMArray.h"
+#include "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIProperties.h"
+#include "mozilla/Attributes.h"
+#include "nsSuiteCID.h"
+
+#define NS_APP_BOOKMARKS_50_FILE "BMarks"
+
+class nsSuiteDirectoryProvider final : public nsIDirectoryServiceProvider2
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+private:
+ ~nsSuiteDirectoryProvider() {}
+
+ void EnsureProfileFile(const nsACString& aLeafName,
+ nsIFile* aParentDir, nsIFile* aTarget);
+
+ void AppendDistroSearchDirs(nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array);
+
+ void AppendFileKey(const char *key, nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array);
+
+ class AppendingEnumerator final : public nsISimpleEnumerator
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ AppendingEnumerator(nsISimpleEnumerator* aBase,
+ const char* const aLeafName);
+
+ private:
+ ~AppendingEnumerator() {}
+ void GetNext();
+
+ nsCOMPtr<nsISimpleEnumerator> mBase;
+ nsDependentCString mLeafName;
+ nsCOMPtr<nsIFile> mNext;
+ };
+};
+
+#endif