summaryrefslogtreecommitdiff
path: root/browser/components/preferences
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/preferences')
-rw-r--r--browser/components/preferences/advanced.js726
-rw-r--r--browser/components/preferences/advanced.xul448
-rw-r--r--browser/components/preferences/applicationManager.js97
-rw-r--r--browser/components/preferences/applicationManager.xul59
-rw-r--r--browser/components/preferences/applications.js1876
-rw-r--r--browser/components/preferences/applications.xul99
-rw-r--r--browser/components/preferences/colors.xul114
-rw-r--r--browser/components/preferences/connection.js199
-rw-r--r--browser/components/preferences/connection.xul159
-rw-r--r--browser/components/preferences/content.js186
-rw-r--r--browser/components/preferences/content.xul209
-rw-r--r--browser/components/preferences/cookies.js943
-rw-r--r--browser/components/preferences/cookies.xul103
-rw-r--r--browser/components/preferences/fonts.js143
-rw-r--r--browser/components/preferences/fonts.xul275
-rw-r--r--browser/components/preferences/handlers.css25
-rw-r--r--browser/components/preferences/handlers.xml81
-rw-r--r--browser/components/preferences/jar.mn44
-rw-r--r--browser/components/preferences/languages.js303
-rw-r--r--browser/components/preferences/languages.xul94
-rw-r--r--browser/components/preferences/main.js543
-rw-r--r--browser/components/preferences/main.xul216
-rw-r--r--browser/components/preferences/moz.build13
-rw-r--r--browser/components/preferences/newtaburl.js102
-rw-r--r--browser/components/preferences/permissions.js459
-rw-r--r--browser/components/preferences/permissions.xul85
-rw-r--r--browser/components/preferences/preferences.xul77
-rw-r--r--browser/components/preferences/privacy.js458
-rw-r--r--browser/components/preferences/privacy.xul256
-rw-r--r--browser/components/preferences/sanitize.js11
-rw-r--r--browser/components/preferences/sanitize.xul108
-rw-r--r--browser/components/preferences/security.js235
-rw-r--r--browser/components/preferences/security.xul177
-rw-r--r--browser/components/preferences/selectBookmark.js82
-rw-r--r--browser/components/preferences/selectBookmark.xul44
-rw-r--r--browser/components/preferences/sync.js192
-rw-r--r--browser/components/preferences/sync.xul178
-rw-r--r--browser/components/preferences/tabs.js89
-rw-r--r--browser/components/preferences/tabs.xul101
39 files changed, 9609 insertions, 0 deletions
diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js
new file mode 100644
index 000000000..9fd7e9943
--- /dev/null
+++ b/browser/components/preferences/advanced.js
@@ -0,0 +1,726 @@
+// 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/.
+
+// Load DownloadUtils module for convertByteUnits
+Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/LoadContextInfo.jsm");
+Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
+
+var gAdvancedPane = {
+ _inited: false,
+
+ /**
+ * Brings the appropriate tab to the front and initializes various bits of UI.
+ */
+ init: function()
+ {
+ this._inited = true;
+ var advancedPrefs = document.getElementById("advancedPrefs");
+
+ var extraArgs = window.arguments[1];
+ if (extraArgs && extraArgs["advancedTab"]){
+ advancedPrefs.selectedTab = document.getElementById(extraArgs["advancedTab"]);
+ } else {
+ var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex");
+ if (preference.value !== null)
+ advancedPrefs.selectedIndex = preference.value;
+ }
+
+#ifdef MOZ_UPDATER
+ this.updateReadPrefs();
+#endif
+ this.updateOfflineAppsPermissions();
+ this.updateOfflineApps();
+
+ this.updateActualCacheSize();
+ this.updateActualAppCacheSize();
+
+ this.updateHWADisplay();
+
+ this.updateUAODisplay();
+
+ // Notify observers that the UI is now ready
+ Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
+ },
+
+ /**
+ * Stores the identity of the current tab in preferences so that the selected
+ * tab can be persisted between openings of the preferences window.
+ */
+ tabSelectionChanged: function()
+ {
+ if (!this._inited)
+ return;
+ var advancedPrefs = document.getElementById("advancedPrefs");
+ var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex");
+ preference.valueFromPreferences = advancedPrefs.selectedIndex;
+ },
+
+ // GENERAL TAB
+
+ /*
+ * Preferences:
+ *
+ * accessibility.browsewithcaret
+ * - true enables keyboard navigation and selection within web pages using a
+ * visible caret, false uses normal keyboard navigation with no caret
+ * accessibility.typeaheadfind
+ * - when set to true, typing outside text areas and input boxes will
+ * automatically start searching for what's typed within the current
+ * document; when set to false, no search action happens
+ * general.autoScroll
+ * - when set to true, clicking the scroll wheel on the mouse activates a
+ * mouse mode where moving the mouse down scrolls the document downward with
+ * speed correlated with the distance of the cursor from the original
+ * position at which the click occurred (and likewise with movement upward);
+ * if false, this behavior is disabled
+ * general.smoothScroll
+ * - set to true to enable finer page scrolling than line-by-line on page-up,
+ * page-down, and other such page movements
+ * layout.spellcheckDefault
+ * - an integer:
+ * 0 disables spellchecking
+ * 1 enables spellchecking, but only for multiline text fields
+ * 2 enables spellchecking for all text fields
+ */
+
+ /**
+ * Stores the original value of the spellchecking preference to enable proper
+ * restoration if unchanged (since we're mapping a tristate onto a checkbox).
+ */
+ _storedSpellCheck: 0,
+
+ /**
+ * Returns true if any spellchecking is enabled and false otherwise, caching
+ * the current value to enable proper pref restoration if the checkbox is
+ * never changed.
+ */
+ readCheckSpelling: function()
+ {
+ var pref = document.getElementById("layout.spellcheckDefault");
+ this._storedSpellCheck = pref.value;
+
+ return (pref.value != 0);
+ },
+
+ /**
+ * Returns the value of the spellchecking preference represented by UI,
+ * preserving the preference's "hidden" value if the preference is
+ * unchanged and represents a value not strictly allowed in UI.
+ */
+ writeCheckSpelling: function()
+ {
+ var checkbox = document.getElementById("checkSpelling");
+ return checkbox.checked ? (this._storedSpellCheck == 2 ? 2 : 1) : 0;
+ },
+
+ /**
+ * security.OCSP.enabled is an integer value for legacy reasons.
+ * A value of 1 means OCSP is enabled. Any other value means it is disabled.
+ */
+ readEnableOCSP: function()
+ {
+ var preference = document.getElementById("security.OCSP.enabled");
+ // This is the case if the preference is the default value.
+ if (preference.value === undefined) {
+ return true;
+ }
+ return preference.value == 1;
+ },
+
+ /**
+ * See documentation for readEnableOCSP.
+ */
+ writeEnableOCSP: function()
+ {
+ var checkbox = document.getElementById("enableOCSP");
+ return checkbox.checked ? 1 : 0;
+ },
+
+ /**
+ * When the user toggles the layers.acceleration.disabled pref,
+ * sync its new value to the gfx.direct2d.disabled pref too.
+ */
+ updateHardwareAcceleration: function()
+ {
+#ifdef XP_WIN
+ var fromPref = document.getElementById("layers.acceleration.enabled");
+ var toPref = document.getElementById("gfx.direct2d.enabled");
+ toPref.value = fromPref.value;
+#endif
+ this.updateHWADisplay();
+ },
+
+ updateHWADisplay: function()
+ {
+#ifdef XP_LINUX
+ let HWA = document.getElementById("layers.acceleration.enabled");
+ document.getElementById("forceHWAccel").disabled = !HWA.value;
+#endif
+ },
+
+ updateUAODisplay: function()
+ {
+ let GUAO = Services.prefs.getCharPref("network.http.useragent.global_override", "");
+ let overridden = (GUAO != "");
+ document.getElementById("UACompatGroup").hidden = overridden;
+ document.getElementById("GUAOwarning").hidden = !overridden;
+ },
+
+ GUAOReset: function()
+ {
+ Services.prefs.clearUserPref("network.http.useragent.global_override");
+ this.updateUAODisplay();
+ },
+
+ // DATA CHOICES TAB
+
+ /**
+ * opening links behind a modal dialog is poor form. Work around flawed text-link handling here.
+ */
+ openTextLink: function(evt) {
+ let where = Services.prefs.getBoolPref("browser.preferences.instantApply") ? "tab" : "window";
+ openUILinkIn(evt.target.getAttribute("href"), where);
+ evt.preventDefault();
+ },
+
+ /**
+ * Set up or hide the Learn More links for various data collection options
+ */
+ _setupLearnMoreLink: function(pref, element) {
+ // set up the Learn More link with the correct URL
+ let url = Services.prefs.getCharPref(pref);
+ let el = document.getElementById(element);
+
+ if (url) {
+ el.setAttribute("href", url);
+ } else {
+ el.setAttribute("hidden", "true");
+ }
+ },
+
+ // NETWORK TAB
+
+ /*
+ * Preferences:
+ *
+ * browser.cache.disk.capacity
+ * - the size of the browser cache in KB
+ * - Only used if browser.cache.disk.smart_size.enabled is disabled
+ */
+
+ /**
+ * Displays a dialog in which proxy settings may be changed.
+ */
+ showConnections: function()
+ {
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/connection.xul",
+ "", null);
+ },
+
+ // Retrieves the amount of space currently used by disk cache
+ updateActualCacheSize: function()
+ {
+ var sum = 0;
+ function updateUI(consumption) {
+ var actualSizeLabel = document.getElementById("actualDiskCacheSize");
+ var sizeStrings = DownloadUtils.convertByteUnits(consumption);
+ var prefStrBundle = document.getElementById("bundlePreferences");
+ var sizeStr = prefStrBundle.getFormattedString("actualDiskCacheSize", sizeStrings);
+ actualSizeLabel.value = sizeStr;
+ }
+
+ Visitor.prototype = {
+ expected: 0,
+ sum: 0,
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsICacheStorageVisitor)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+ onCacheStorageInfo: function(num, consumption)
+ {
+ this.sum += consumption;
+ if (!--this.expected)
+ updateUI(this.sum);
+ }
+ };
+ function Visitor(callbacksExpected) {
+ this.expected = callbacksExpected;
+ }
+
+ var cacheService =
+ Components.classes["@mozilla.org/netwerk/cache-storage-service;1"]
+ .getService(Components.interfaces.nsICacheStorageService);
+ // non-anonymous
+ var storage1 = cacheService.diskCacheStorage(LoadContextInfo.default, false);
+ // anonymous
+ var storage2 = cacheService.diskCacheStorage(LoadContextInfo.anonymous, false);
+
+ // expect 2 callbacks
+ var visitor = new Visitor(2);
+ storage1.asyncVisitStorage(visitor, false /* Do not walk entries */);
+ storage2.asyncVisitStorage(visitor, false /* Do not walk entries */);
+ },
+
+ // Retrieves the amount of space currently used by offline cache
+ updateActualAppCacheSize: function()
+ {
+ var visitor = {
+ onCacheStorageInfo: function(aEntryCount, aConsumption, aCapacity, aDiskDirectory)
+ {
+ var actualSizeLabel = document.getElementById("actualAppCacheSize");
+ var sizeStrings = DownloadUtils.convertByteUnits(aConsumption);
+ var prefStrBundle = document.getElementById("bundlePreferences");
+ var sizeStr = prefStrBundle.getFormattedString("actualAppCacheSize", sizeStrings);
+ actualSizeLabel.value = sizeStr;
+ }
+ };
+
+ var cacheService =
+ Components.classes["@mozilla.org/netwerk/cache-storage-service;1"]
+ .getService(Components.interfaces.nsICacheStorageService);
+ var storage = cacheService.appCacheStorage(LoadContextInfo.default, null);
+ try {
+ storage.asyncVisitStorage(visitor, false);
+ } catch(ex) {
+ // Service unavailable: user most likely crippled the cache.
+ }
+ },
+
+ updateCacheSizeUI: function(smartSizeEnabled)
+ {
+ document.getElementById("useCacheBefore").disabled = smartSizeEnabled;
+ document.getElementById("cacheSize").disabled = smartSizeEnabled;
+ document.getElementById("useCacheAfter").disabled = smartSizeEnabled;
+ },
+
+ readSmartSizeEnabled: function()
+ {
+ // The smart_size.enabled preference element is inverted="true", so its
+ // value is the opposite of the actual pref value
+ var disabled = document.getElementById("browser.cache.disk.smart_size.enabled").value;
+ this.updateCacheSizeUI(!disabled);
+ },
+
+ /**
+ * Converts the cache size from units of KB to units of MB and returns that
+ * value.
+ */
+ readCacheSize: function()
+ {
+ var preference = document.getElementById("browser.cache.disk.capacity");
+ return preference.value / 1024;
+ },
+
+ /**
+ * Converts the cache size as specified in UI (in MB) to KB and returns that
+ * value.
+ */
+ writeCacheSize: function()
+ {
+ var cacheSize = document.getElementById("cacheSize");
+ var intValue = parseInt(cacheSize.value, 10);
+ return isNaN(intValue) ? 0 : intValue * 1024;
+ },
+
+ /**
+ * Clears the cache.
+ */
+ clearCache: function()
+ {
+ var cache = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"]
+ .getService(Components.interfaces.nsICacheStorageService);
+ try {
+ cache.clear();
+ } catch(ex) {}
+ this.updateActualCacheSize();
+ },
+
+ /**
+ * Clears the application cache.
+ */
+ clearOfflineAppCache: function()
+ {
+ Components.utils.import("resource:///modules/offlineAppCache.jsm");
+ OfflineAppCacheHelper.clear();
+
+ this.updateActualAppCacheSize();
+ this.updateOfflineApps();
+ },
+
+ updateOfflineAppsPermissions: function()
+ {
+ var permPref = document.getElementById("offline-apps.permissions");
+ var allowPref = document.getElementById("offline-apps.allow_by_default");
+ var notifyPref = document.getElementById("browser.offline-apps.notify");
+ switch (permPref.value) {
+ case 0: allowPref.value = false;
+ notifyPref.value = false;
+ break;
+ case 1: allowPref.value = false;
+ notifyPref.value = true;
+ break;
+ case 2: allowPref.value = true;
+ notifyPref.value = true;
+ break;
+ default: console.error("Preference error: Invalid value ",permPref.value," for offline app permissions - resetting to default.");
+ permPref.value = 2;
+ allowPref.value = true;
+ notifyPref.value = true;
+ }
+ // Set state of "Exceptions" button accordingly.
+ var button = document.getElementById("offlineNotifyExceptions");
+ button.disabled = !allowPref.value && !notifyPref.value;
+ },
+
+ showOfflineExceptions: function()
+ {
+ var bundlePreferences = document.getElementById("bundlePreferences");
+ var params = { blockVisible : false,
+ sessionVisible : false,
+ allowVisible : false,
+ prefilledHost : "",
+ permissionType : "offline-app",
+ manageCapability : Components.interfaces.nsIPermissionManager.DENY_ACTION,
+ windowTitle : bundlePreferences.getString("offlinepermissionstitle"),
+ introText : bundlePreferences.getString("offlinepermissionstext") };
+ document.documentElement.openWindow("Browser:Permissions",
+ "chrome://browser/content/preferences/permissions.xul",
+ "", params);
+ },
+
+ // XXX: duplicated in browser.js
+ _getOfflineAppUsage: function(perm, groups)
+ {
+ var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
+ getService(Components.interfaces.nsIApplicationCacheService);
+ if (!groups) {
+ try {
+ groups = cacheService.getGroups();
+ } catch(ex) {
+ // Cache disabled.
+ return 0;
+ }
+ }
+
+ var ios = Components.classes["@mozilla.org/network/io-service;1"].
+ getService(Components.interfaces.nsIIOService);
+
+ var usage = 0;
+ for (var i = 0; i < groups.length; i++) {
+ var uri = ios.newURI(groups[i], null, null);
+ if (perm.matchesURI(uri, true)) {
+ var cache = cacheService.getActiveCache(groups[i]);
+ usage += cache.usage;
+ }
+ }
+
+ return usage;
+ },
+
+ /**
+ * Updates the list of offline applications
+ */
+ updateOfflineApps: function()
+ {
+ var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+ .getService(Components.interfaces.nsIPermissionManager);
+
+ var list = document.getElementById("offlineAppsList");
+ while (list.firstChild) {
+ list.removeChild(list.firstChild);
+ }
+
+ var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
+ getService(Components.interfaces.nsIApplicationCacheService);
+
+ try {
+ var groups = cacheService.getGroups();
+
+ var bundle = document.getElementById("bundlePreferences");
+
+ var enumerator = pm.enumerator;
+ while (enumerator.hasMoreElements()) {
+ var perm = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
+ if (perm.type == "offline-app" &&
+ perm.capability != Components.interfaces.nsIPermissionManager.DEFAULT_ACTION &&
+ perm.capability != Components.interfaces.nsIPermissionManager.DENY_ACTION) {
+ var row = document.createElement("listitem");
+ row.id = "";
+ row.className = "offlineapp";
+ row.setAttribute("origin", perm.principal.origin);
+ var converted = DownloadUtils.
+ convertByteUnits(this._getOfflineAppUsage(perm, groups));
+ row.setAttribute("usage",
+ bundle.getFormattedString("offlineAppUsage",
+ converted));
+ list.appendChild(row);
+ }
+ }
+ } catch(ex) {
+ // Cache service unavailable/errored, off-line app cache is disabled or 0
+ // Do nothing, just leave the box blank.
+ }
+ },
+
+ offlineAppSelected: function()
+ {
+ var removeButton = document.getElementById("offlineAppsListRemove");
+ var list = document.getElementById("offlineAppsList");
+ if (list.selectedItem) {
+ removeButton.setAttribute("disabled", "false");
+ } else {
+ removeButton.setAttribute("disabled", "true");
+ }
+ },
+
+ removeOfflineApp: function()
+ {
+ var list = document.getElementById("offlineAppsList");
+ var item = list.selectedItem;
+ var origin = item.getAttribute("origin");
+ var principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
+
+ var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Components.interfaces.nsIPromptService);
+ var flags = prompts.BUTTON_TITLE_IS_STRING * prompts.BUTTON_POS_0 +
+ prompts.BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_1;
+
+ var bundle = document.getElementById("bundlePreferences");
+ var title = bundle.getString("offlineAppRemoveTitle");
+ var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]);
+ var confirm = bundle.getString("offlineAppRemoveConfirm");
+ var result = prompts.confirmEx(window, title, prompt, flags, confirm,
+ null, null, null, {});
+ if (result != 0)
+ return;
+
+ // get the permission
+ var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+ .getService(Components.interfaces.nsIPermissionManager);
+ var perm = pm.getPermissionObject(principal, "offline-app", true);
+ if (perm) {
+ // clear offline cache entries
+ try {
+ var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
+ getService(Components.interfaces.nsIApplicationCacheService);
+ var groups = cacheService.getGroups();
+ for (var i = 0; i < groups.length; i++) {
+ var uri = Services.io.newURI(groups[i], null, null);
+ if (perm.matchesURI(uri, true)) {
+ var cache = cacheService.getActiveCache(groups[i]);
+ cache.discard();
+ }
+ }
+ } catch (e) {}
+
+ pm.removePermission(perm);
+ }
+ list.removeChild(item);
+ gAdvancedPane.offlineAppSelected();
+ this.updateActualAppCacheSize();
+ },
+
+ // UPDATE TAB
+
+ /*
+ * Preferences:
+ *
+ * app.update.enabled
+ * - true if updates to the application are enabled, false otherwise
+ * extensions.update.enabled
+ * - true if updates to extensions and themes are enabled, false otherwise
+ * browser.search.update
+ * - true if updates to search engines are enabled, false otherwise
+ * app.update.auto
+ * - true if updates should be automatically downloaded and installed,
+ * possibly with a warning if incompatible extensions are installed (see
+ * app.update.mode); false if the user should be asked what he wants to do
+ * when an update is available
+ * app.update.mode
+ * - an integer:
+ * 0 do not warn if an update will disable extensions or themes
+ * 1 warn if an update will disable extensions or themes
+ * 2 warn if an update will disable extensions or themes *or* if the
+ * update is a major update
+ */
+
+#ifdef MOZ_UPDATER
+ /**
+ * Selects the item of the radiogroup, and sets the warnIncompatible checkbox
+ * based on the pref values and locked states.
+ *
+ * UI state matrix for update preference conditions
+ *
+ * UI Components: Preferences
+ * Radiogroup i = app.update.enabled
+ * Warn before disabling extensions checkbox ii = app.update.auto
+ * iii = app.update.mode
+ *
+ * Disabled states:
+ * Element pref value locked disabled
+ * radiogroup i t/f f false
+ * i t/f *t* *true*
+ * ii t/f f false
+ * ii t/f *t* *true*
+ * iii 0/1/2 t/f false
+ * warnIncompatible i t f false
+ * i t *t* *true*
+ * i *f* t/f *true*
+ * ii t f false
+ * ii t *t* *true*
+ * ii *f* t/f *true*
+ * iii 0/1/2 f false
+ * iii 0/1/2 *t* *true*
+ */
+ updateReadPrefs: function()
+ {
+ var enabledPref = document.getElementById("app.update.enabled");
+ var autoPref = document.getElementById("app.update.auto");
+ var radiogroup = document.getElementById("updateRadioGroup");
+
+ if (!enabledPref.value) // Don't care for autoPref.value in this case.
+ radiogroup.value="manual"; // 3. Never check for updates.
+ else if (autoPref.value) // enabledPref.value && autoPref.value
+ radiogroup.value="auto"; // 1. Automatically install updates for Desktop only
+ else // enabledPref.value && !autoPref.value
+ radiogroup.value="checkOnly"; // 2. Check, but let me choose
+
+ var canCheck = Components.classes["@mozilla.org/updates/update-service;1"].
+ getService(Components.interfaces.nsIApplicationUpdateService).
+ canCheckForUpdates;
+ // canCheck is false if the enabledPref is false and locked,
+ // or the binary platform or OS version is not known.
+ // A locked pref is sufficient to disable the radiogroup.
+ radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
+
+ var modePref = document.getElementById("app.update.mode");
+ var warnIncompatible = document.getElementById("warnIncompatible");
+ // the warnIncompatible checkbox value is set by readAddonWarn
+ warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
+ !enabledPref.value || !autoPref.value;
+ },
+
+ /**
+ * Sets the pref values based on the selected item of the radiogroup,
+ * and sets the disabled state of the warnIncompatible checkbox accordingly.
+ */
+ updateWritePrefs: function()
+ {
+ var enabledPref = document.getElementById("app.update.enabled");
+ var autoPref = document.getElementById("app.update.auto");
+ var radiogroup = document.getElementById("updateRadioGroup");
+ switch (radiogroup.value) {
+ case "auto": // 1. Automatically install updates for Desktop only
+ enabledPref.value = true;
+ autoPref.value = true;
+ break;
+ case "checkOnly": // 2. Check, but let me choose
+ enabledPref.value = true;
+ autoPref.value = false;
+ break;
+ case "manual": // 3. Never check for updates.
+ enabledPref.value = false;
+ autoPref.value = false;
+ }
+
+ var warnIncompatible = document.getElementById("warnIncompatible");
+ var modePref = document.getElementById("app.update.mode");
+ warnIncompatible.disabled = enabledPref.locked || !enabledPref.value ||
+ autoPref.locked || !autoPref.value ||
+ modePref.locked;
+
+ },
+
+ /**
+ * Stores the value of the app.update.mode preference, which is a tristate
+ * integer preference. We store the value here so that we can properly
+ * restore the preference value if the UI reflecting the preference value
+ * is in a state which can represent either of two integer values (as
+ * opposed to only one possible value in the other UI state).
+ */
+ _modePreference: -1,
+
+ /**
+ * Reads the app.update.mode preference and converts its value into a
+ * true/false value for use in determining whether the "Warn me if this will
+ * disable extensions or themes" checkbox is checked. We also save the value
+ * of the preference so that the preference value can be properly restored if
+ * the user's preferences cannot adequately be expressed by a single checkbox.
+ *
+ * app.update.mode Checkbox State Meaning
+ * 0 Unchecked Do not warn
+ * 1 Checked Warn if there are incompatibilities
+ * 2 Checked Warn if there are incompatibilities,
+ * or the update is major.
+ */
+ readAddonWarn: function()
+ {
+ var preference = document.getElementById("app.update.mode");
+ var warn = preference.value != 0;
+ gAdvancedPane._modePreference = warn ? preference.value : 1;
+ return warn;
+ },
+
+ /**
+ * Converts the state of the "Warn me if this will disable extensions or
+ * themes" checkbox into the integer preference which represents it,
+ * returning that value.
+ */
+ writeAddonWarn: function()
+ {
+ var warnIncompatible = document.getElementById("warnIncompatible");
+ return !warnIncompatible.checked ? 0 : gAdvancedPane._modePreference;
+ },
+
+ /**
+ * Displays the history of installed updates.
+ */
+ showUpdates: function()
+ {
+ var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"]
+ .createInstance(Components.interfaces.nsIUpdatePrompt);
+ prompter.showUpdateHistory(window);
+ },
+#endif
+
+ // CERTIFICATES TAB
+
+ /*
+ * Preferences:
+ *
+ * security.default_personal_cert
+ * - a string:
+ * "Select Automatically" select a certificate automatically when a site
+ * requests one
+ * "Ask Every Time" present a dialog to the user so he can select
+ * the certificate to use on a site which
+ * requests one
+ */
+
+ /**
+ * Displays the user's certificates and associated options.
+ */
+ showCertificates: function()
+ {
+ document.documentElement.openWindow("mozilla:certmanager",
+ "chrome://pippki/content/certManager.xul",
+ "", null);
+ },
+
+ /**
+ * Displays a dialog from which the user can manage his security devices.
+ */
+ showSecurityDevices: function()
+ {
+ document.documentElement.openWindow("mozilla:devicemanager",
+ "chrome://pippki/content/device_manager.xul",
+ "", null);
+ }
+};
diff --git a/browser/components/preferences/advanced.xul b/browser/components/preferences/advanced.xul
new file mode 100644
index 000000000..cfc857aed
--- /dev/null
+++ b/browser/components/preferences/advanced.xul
@@ -0,0 +1,448 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % advancedDTD SYSTEM "chrome://browser/locale/preferences/advanced.dtd">
+%advancedDTD;
+<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
+%privacyDTD;
+]>
+
+<overlay id="AdvancedPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneAdvanced" onpaneload="gAdvancedPane.init();">
+
+ <preferences id="advancedPreferences">
+ <preference id="browser.preferences.advanced.selectedTabIndex"
+ name="browser.preferences.advanced.selectedTabIndex"
+ type="int"/>
+
+ <!--XXX button prefs -->
+
+ <!-- General tab -->
+ <preference id="accessibility.typeaheadfind" name="accessibility.typeaheadfind" type="bool"/>
+
+ <preference id="general.autoScroll" name="general.autoScroll" type="bool"/>
+ <preference id="general.smoothScroll" name="general.smoothScroll" type="bool"/>
+ <preference id="layers.acceleration.enabled" name="layers.acceleration.enabled" type="bool"
+ onchange="gAdvancedPane.updateHardwareAcceleration()"/>
+ <preference id="layers.acceleration.force" name="layers.acceleration.force" type="bool"/>
+#ifdef XP_WIN
+ <preference id="gfx.direct2d.enabled" name="gfx.direct2d.disabled" type="bool" inverted="true"/>
+#endif
+ <preference id="layout.spellcheckDefault" name="layout.spellcheckDefault" type="int"/>
+
+ <preference id="pref.general.compatmode" name="general.useragent.compatMode" type="int"/>
+
+ <preference id="pref.general.captiveportal" name="network.captive-portal-service.enabled" type="bool"/>
+
+ <!-- Network tab -->
+ <preference id="browser.cache.disk.capacity" name="browser.cache.disk.capacity" type="int"/>
+
+ <preference id="browser.cache.disk.smart_size.enabled"
+ name="browser.cache.disk.smart_size.enabled"
+ inverted="true"
+ type="bool"/>
+
+ <preference id="offline-apps.permissions" name="offline-apps.permissions" type="int"
+ onchange="gAdvancedPane.updateOfflineAppsPermissions()"/>
+ <preference id="browser.offline-apps.notify" name="browser.offline-apps.notify" type="bool"/>
+ <preference id="offline-apps.allow_by_default" name="offline-apps.allow_by_default" type="bool"/>
+
+ <!-- Update tab -->
+#ifdef MOZ_UPDATER
+ <preference id="app.update.enabled" name="app.update.enabled" type="bool"/>
+ <preference id="app.update.auto" name="app.update.auto" type="bool"/>
+ <preference id="app.update.mode" name="app.update.mode" type="int"/>
+
+ <preference id="app.update.disable_button.showUpdateHistory"
+ name="app.update.disable_button.showUpdateHistory"
+ type="bool"/>
+#endif
+
+ <preference id="browser.search.update" name="browser.search.update" type="bool"/>
+
+ <!-- Certificates tab -->
+ <preference id="security.default_personal_cert" name="security.default_personal_cert" type="string"/>
+
+ <preference id="security.disable_button.openCertManager"
+ name="security.disable_button.openCertManager"
+ type="bool"/>
+ <preference id="security.disable_button.openDeviceManager"
+ name="security.disable_button.openDeviceManager"
+ type="bool"/>
+ <preference id="security.OCSP.enabled"
+ name="security.OCSP.enabled"
+ type="int"/>
+ <preference id="security.OCSP.require"
+ name="security.OCSP.require"
+ type="bool"/>
+
+ <!-- Pale Moon: smooth scrolling tab -->
+ <preference id="general.smoothScroll.lines" name="general.smoothScroll.lines" type="bool"/>
+ <preference id="general.smoothScroll.lines.durationMinMS" name="general.smoothScroll.lines.durationMinMS" type="int"/>
+ <preference id="general.smoothScroll.lines.durationMaxMS" name="general.smoothScroll.lines.durationMaxMS" type="int"/>
+ <preference id="general.smoothScroll.pages" name="general.smoothScroll.pages" type="bool"/>
+ <preference id="general.smoothScroll.pages.durationMinMS" name="general.smoothScroll.pages.durationMinMS" type="int"/>
+ <preference id="general.smoothScroll.pages.durationMaxMS" name="general.smoothScroll.pages.durationMaxMS" type="int"/>
+ <preference id="general.smoothScroll.mouseWheel" name="general.smoothScroll.mouseWheel" type="bool"/>
+ <preference id="general.smoothScroll.mouseWheel.durationMinMS" name="general.smoothScroll.mouseWheel.durationMinMS" type="int"/>
+ <preference id="general.smoothScroll.mouseWheel.durationMaxMS" name="general.smoothScroll.mouseWheel.durationMaxMS" type="int"/>
+ <preference id="general.smoothScroll.scrollbars" name="general.smoothScroll.scrollbars" type="bool"/>
+ <preference id="general.smoothScroll.scrollbars.durationMinMS" name="general.smoothScroll.scrollbars.durationMinMS" type="int"/>
+ <preference id="general.smoothScroll.scrollbars.durationMaxMS" name="general.smoothScroll.scrollbars.durationMaxMS" type="int"/>
+
+ <preference id="mousewheel.default.delta_multiplier_y" name="mousewheel.default.delta_multiplier_y" type="int"/>
+ </preferences>
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/advanced.js"/>
+
+ <tabbox id="advancedPrefs" flex="1"
+ onselect="gAdvancedPane.tabSelectionChanged();">
+
+ <tabs id="tabsElement">
+ <tab id="generalTab" label="&generalTab.label;" helpTopic="prefs-advanced-general"/>
+ <tab id="networkTab" label="&networkTab.label;" helpTopic="prefs-advanced-network"/>
+ <tab id="updateTab" label="&updateTab.label;" helpTopic="prefs-advanced-update"/>
+ <tab id="encryptionTab" label="&certificateTab.label;" helpTopic="prefs-advanced-encryption"/>
+ <tab id="scrollparamTab" label="&scrollparamTab.label;" helpTopic="prefs-advanced-scrollparams"/>
+ </tabs>
+
+ <tabpanels flex="1">
+
+ <!-- General -->
+ <tabpanel id="generalPanel" orient="vertical">
+
+ <!-- Accessibility -->
+ <groupbox id="accessibilityGroup" align="start">
+ <caption label="&accessibility.label;"/>
+
+ <checkbox id="searchStartTyping"
+ label="&searchStartTyping.label;"
+ accesskey="&searchStartTyping.accesskey;"
+ preference="accessibility.typeaheadfind"/>
+ </groupbox>
+
+ <!-- Browsing -->
+ <groupbox id="browsingGroup" align="start">
+ <caption label="&browsing.label;"/>
+
+ <checkbox id="useAutoScroll"
+ label="&useAutoScroll.label;"
+ accesskey="&useAutoScroll.accesskey;"
+ preference="general.autoScroll"/>
+ <checkbox id="checkSpelling"
+ label="&checkSpelling.label;"
+ accesskey="&checkSpelling.accesskey;"
+ onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
+ onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
+ preference="layout.spellcheckDefault"/>
+ </groupbox>
+
+ <!-- Hardware Acceleration -->
+ <groupbox id="browsingGroup" align="start">
+ <caption label="&HWAccel.label;"/>
+ <label>&restartRequired.label;</label>
+ <checkbox id="allowHWAccel"
+ label="&allowHWAccel.label;"
+ accesskey="&allowHWAccel.accesskey;"
+ preference="layers.acceleration.enabled"/>
+#ifdef XP_LINUX
+ <checkbox id="forceHWAccel" class="indent"
+ label="&forceHWAccel.label;"
+ preference="layers.acceleration.force"/>
+#endif
+ </groupbox>
+
+ <!-- User Agent compatibility -->
+ <hbox id="GUAOwarning" align="center" hidden="true">
+ <label style="color:red;" id="UAWarning">&UAWarning.label;</label>
+ <button label="&UAWarning.reset;" oncommand="gAdvancedPane.GUAOReset();" />
+ </hbox>
+ <groupbox id="UACompatGroup" orient="vertical">
+ <caption label="&UACompatGroup.label;"/>
+ <hbox align="center">
+ <label id="UACompat" control="UACompat-menu">&UACompat.label;</label>
+ <menulist id="UACompat-menu" preference="pref.general.compatmode" sizetopopup="always">
+ <menupopup>
+ <menuitem label="&UACompat.Native;" value="0" />
+ <menuitem label="&UACompat.Gecko;" value="1" />
+ <menuitem label="&UACompat.Firefox;" value="2" />
+ </menupopup>
+ </menulist>
+ </hbox>
+ </groupbox>
+
+ <!-- Captive portal detection -->
+ <groupbox id="captivePortalGroup" orient="vertical">
+ <caption label="&captivePortalGroup.label;"/>
+ <checkbox id="captivePortalDetect"
+ label="&captivePortalDetect.label;"
+ preference="pref.general.captiveportal"/>
+ </groupbox>
+
+ </tabpanel>
+
+ <!-- Network -->
+ <tabpanel id="networkPanel" orient="vertical">
+
+ <!-- Connection -->
+ <groupbox id="connectionGroup">
+ <caption label="&connection.label;"/>
+
+ <hbox align="center">
+ <description flex="1" control="connectionSettings">&connectionDesc.label;</description>
+ <button id="connectionSettings" icon="network" label="&connectionSettings.label;"
+ accesskey="&connectionSettings.accesskey;"
+ oncommand="gAdvancedPane.showConnections();"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Cache -->
+ <groupbox id="cacheGroup">
+ <caption label="&httpCache.label;"/>
+
+ <hbox align="center">
+ <label id="actualDiskCacheSize" flex="1"/>
+ <button id="clearCacheButton" icon="clear"
+ label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;"
+ oncommand="gAdvancedPane.clearCache();"/>
+ </hbox>
+ <checkbox preference="browser.cache.disk.smart_size.enabled"
+ id="allowSmartSize" flex="1"
+ onsyncfrompreference="return gAdvancedPane.readSmartSizeEnabled();"
+ label="&overrideSmartCacheSize.label;"
+ accesskey="&overrideSmartCacheSize.accesskey;"/>
+ <hbox align="center" class="indent">
+ <label id="useCacheBefore" control="cacheSize"
+ accesskey="&limitCacheSizeBefore.accesskey;"
+ value="&limitCacheSizeBefore.label;"/>
+ <textbox id="cacheSize" type="number" size="4" max="1024"
+ preference="browser.cache.disk.capacity"
+ onsyncfrompreference="return gAdvancedPane.readCacheSize();"
+ onsynctopreference="return gAdvancedPane.writeCacheSize();"
+ aria-labelledby="useCacheBefore cacheSize useCacheAfter"/>
+ <label id="useCacheAfter" flex="1">&limitCacheSizeAfter.label;</label>
+ </hbox>
+ </groupbox>
+
+ <!-- Offline apps -->
+ <groupbox id="offlineGroup">
+ <caption label="&offlineStorage2.label;"/>
+
+ <hbox align="center">
+ <label id="actualAppCacheSize" flex="1"/>
+ <button id="clearOfflineAppCacheButton" icon="clear"
+ label="&clearOfflineAppCacheNow.label;" accesskey="&clearOfflineAppCacheNow.accesskey;"
+ oncommand="gAdvancedPane.clearOfflineAppCache();"/>
+ </hbox>
+ <label id="offlineAppsPermsLabel">&offlineAppsPermissions.label;</label>
+ <hbox align="center">
+ <menulist id="offlineAppsPerms-menu" preference="offline-apps.permissions" sizetopopup="always">
+ <menupopup>
+ <menuitem label="&offlineAppsPermissions.Allow;" value="2" />
+ <menuitem label="&offlineAppsPermissions.Ask;" value="1" />
+ <menuitem label="&offlineAppsPermissions.Deny;" value="0" />
+ </menupopup>
+ </menulist>
+ <spacer flex="1"/>
+ <button id="offlineNotifyExceptions"
+ label="&offlineNotifyExceptions.label;"
+ accesskey="&offlineNotifyExceptions.accesskey;"
+ oncommand="gAdvancedPane.showOfflineExceptions();"/>
+ </hbox>
+ <hbox>
+ <vbox flex="1">
+ <label id="offlineAppsListLabel">&offlineAppsList2.label;</label>
+ <listbox id="offlineAppsList"
+ style="height: &offlineAppsList.height;;"
+ flex="1"
+ aria-labelledby="offlineAppsListLabel"
+ onselect="gAdvancedPane.offlineAppSelected(event);">
+ </listbox>
+ </vbox>
+ <vbox pack="end">
+ <button id="offlineAppsListRemove"
+ disabled="true"
+ label="&offlineAppsListRemove.label;"
+ accesskey="&offlineAppsListRemove.accesskey;"
+ oncommand="gAdvancedPane.removeOfflineApp();"/>
+ </vbox>
+ </hbox>
+ </groupbox>
+ </tabpanel>
+
+ <!-- Update -->
+ <tabpanel id="updatePanel" orient="vertical">
+#ifdef MOZ_UPDATER
+ <groupbox id="updateApp">
+ <caption label="&updateApp.label;"/>
+ <radiogroup id="updateRadioGroup"
+ oncommand="gAdvancedPane.updateWritePrefs();">
+ <radio id="autoDesktop"
+ value="auto"
+ label="&updateAuto1.label;"
+ accesskey="&updateAuto1.accesskey;"/>
+ <hbox class="indent">
+ <checkbox id="warnIncompatible"
+ label="&updateAutoAddonWarn.label;"
+ accesskey="&updateAutoAddonWarn.accesskey;"
+ preference="app.update.mode"
+ onsyncfrompreference="return gAdvancedPane.readAddonWarn();"
+ onsynctopreference="return gAdvancedPane.writeAddonWarn();"/>
+ </hbox>
+ <radio value="checkOnly"
+ label="&updateCheck.label;"
+ accesskey="&updateCheck.accesskey;"/>
+ <radio value="manual"
+ label="&updateManual.label;"
+ accesskey="&updateManual.accesskey;"/>
+ </radiogroup>
+
+ <hbox>
+ <button id="showUpdateHistory"
+ label="&updateHistory.label;"
+ accesskey="&updateHistory.accesskey;"
+ preference="app.update.disable_button.showUpdateHistory"
+ oncommand="gAdvancedPane.showUpdates();"/>
+ </hbox>
+ </groupbox>
+#endif
+ <groupbox id="updateOthers">
+ <caption label="&updateOthers.label;"/>
+ <checkbox id="enableSearchUpdate"
+ label="&enableSearchUpdate.label;"
+ accesskey="&enableSearchUpdate.accesskey;"
+ preference="browser.search.update"/>
+ </groupbox>
+ </tabpanel>
+
+ <!-- Certificates -->
+ <tabpanel id="encryptionPanel" orient="vertical">
+
+ <!--
+ The values on these radio buttons may look like l12y issues, but
+ they're not - this preference uses *those strings* as its values.
+ I KID YOU NOT.
+ -->
+
+ <groupbox>
+ <caption label="&certGroup.label;"/>
+ <description id="CertSelectionDesc" control="certSelection">&certSelection.description;</description>
+ <radiogroup id="certSelection" orient="horizontal" preftype="string"
+ preference="security.default_personal_cert"
+ aria-labelledby="CertSelectionDesc">
+ <radio label="&certs.auto;" accesskey="&certs.auto.accesskey;"
+ value="Select Automatically"/>
+ <radio label="&certs.ask;" accesskey="&certs.ask.accesskey;"
+ value="Ask Every Time"/>
+ </radiogroup>
+ </groupbox>
+ <groupbox>
+ <caption label="&ocspGroup.label;"/>
+ <checkbox id="enableOCSP"
+ label="&enableOCSP.label;"
+ accesskey="&enableOCSP.accesskey;"
+ onsyncfrompreference="return gAdvancedPane.readEnableOCSP();"
+ onsynctopreference="return gAdvancedPane.writeEnableOCSP();"
+ preference="security.OCSP.enabled"/>
+ <checkbox id="requireOCSP"
+ label="&requireOCSP.label;"
+ accesskey="&requireOCSP.accesskey;"
+ preference="security.OCSP.require"/>
+ </groupbox>
+
+ <separator/>
+
+ <hbox>
+ <button id="viewCertificatesButton"
+ label="&viewCerts.label;" accesskey="&viewCerts.accesskey;"
+ oncommand="gAdvancedPane.showCertificates();"
+ preference="security.disable_button.openCertManager"/>
+ <button id="viewSecurityDevicesButton"
+ label="&viewSecurityDevices.label;" accesskey="&viewSecurityDevices.accesskey;"
+ oncommand="gAdvancedPane.showSecurityDevices();"
+ preference="security.disable_button.openDeviceManager"/>
+ </hbox>
+ </tabpanel>
+
+ <!-- Pale Moon: Scrolling tab -->
+ <tabpanel id="scrollparamTab" orient="vertical">
+
+ <checkbox id="useSmoothScrolling"
+ label="&useSmoothScrolling.label;"
+ accesskey="&useSmoothScrolling.accesskey;"
+ preference="general.smoothScroll"/>
+
+ <label>&smoothscroll.explain.label;</label>
+
+ <groupbox>
+ <caption label="&smoothscroll.params.label;"/>
+
+ <checkbox label="&smoothscroll.mousewheel.label;" preference="general.smoothScroll.mouseWheel"/>
+ <hbox align="center" class="indent">
+ <label value="&smoothscroll.mousewheel.duration;"/>
+ <textbox type="number" size="3" max="500"
+ preference="general.smoothScroll.mouseWheel.durationMinMS"/>
+ <label>&smoothscroll.to;</label>
+ <textbox type="number" size="4" max="2000"
+ preference="general.smoothScroll.mouseWheel.durationMaxMS"/>
+ <label flex="1">ms.</label>
+ </hbox>
+
+ <checkbox label="&smoothscroll.arrowkeys.label;" preference="general.smoothScroll.lines"/>
+ <hbox align="center" class="indent">
+ <label value="&smoothscroll.arrowkeys.duration;"/>
+ <textbox type="number" size="3" max="500"
+ preference="general.smoothScroll.lines.durationMinMS"/>
+ <label>&smoothscroll.to;</label>
+ <textbox type="number" size="4" max="2000"
+ preference="general.smoothScroll.lines.durationMaxMS"/>
+ <label flex="1">ms.</label>
+ </hbox>
+
+ <checkbox label="&smoothscroll.pagekeys.label;" preference="general.smoothScroll.pages"/>
+ <hbox align="center" class="indent">
+ <label value="&smoothscroll.pagekeys.duration;"/>
+ <textbox type="number" size="3" max="500"
+ preference="general.smoothScroll.pages.durationMinMS"/>
+ <label>&smoothscroll.to;</label>
+ <textbox type="number" size="4" max="2000"
+ preference="general.smoothScroll.pages.durationMaxMS"/>
+ <label flex="1">ms.</label>
+ </hbox>
+
+ <checkbox label="&smoothscroll.scrollbar.label;" preference="general.smoothScroll.scrollbars"/>
+ <hbox align="center" class="indent">
+ <label value="&smoothscroll.scrollbar.duration;"/>
+ <textbox type="number" size="3" max="500"
+ preference="general.smoothScroll.scrollbars.durationMinMS"/>
+ <label>&smoothscroll.to;</label>
+ <textbox type="number" size="4" max="2000"
+ preference="general.smoothScroll.scrollbars.durationMaxMS"/>
+ <label flex="1">ms.</label>
+ </hbox>
+
+ <hbox align="center">
+ <label value="&smoothscroll.overall.yspeed.label;"/>
+ <textbox type="number" size="3" min="1" max="999"
+ preference="mousewheel.default.delta_multiplier_y"/>
+ <label flex="1">%.</label>
+ </hbox>
+ </groupbox>
+ </tabpanel>
+ <!-- end Smooth scrolling tab -->
+
+ </tabpanels>
+ </tabbox>
+ </prefpane>
+
+</overlay>
diff --git a/browser/components/preferences/applicationManager.js b/browser/components/preferences/applicationManager.js
new file mode 100644
index 000000000..43558c156
--- /dev/null
+++ b/browser/components/preferences/applicationManager.js
@@ -0,0 +1,97 @@
+// 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 gAppManagerDialog = {
+ _removed: [],
+
+ init: function() {
+ this.handlerInfo = window.arguments[0];
+
+ var bundle = document.getElementById("appManagerBundle");
+ var contentText;
+ if (this.handlerInfo.type == TYPE_MAYBE_FEED)
+ contentText = bundle.getString("handleWebFeeds");
+ else {
+ var description = gApplicationsPane._describeType(this.handlerInfo);
+ var key =
+ (this.handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) ? "handleFile"
+ : "handleProtocol";
+ contentText = bundle.getFormattedString(key, [description]);
+ }
+ contentText = bundle.getFormattedString("descriptionApplications", [contentText]);
+ document.getElementById("appDescription").textContent = contentText;
+
+ var list = document.getElementById("appList");
+ var apps = this.handlerInfo.possibleApplicationHandlers.enumerate();
+ while (apps.hasMoreElements()) {
+ let app = apps.getNext();
+ if (!gApplicationsPane.isValidHandlerApp(app))
+ continue;
+
+ app.QueryInterface(Ci.nsIHandlerApp);
+ var item = list.appendItem(app.name);
+ item.setAttribute("image", gApplicationsPane._getIconURLForHandlerApp(app));
+ item.className = "listitem-iconic";
+ item.app = app;
+ }
+
+ list.selectedIndex = 0;
+ },
+
+ onOK: function() {
+ if (!this._removed.length) {
+ // return early to avoid calling the |store| method.
+ return;
+ }
+
+ for (var i = 0; i < this._removed.length; ++i)
+ this.handlerInfo.removePossibleApplicationHandler(this._removed[i]);
+
+ this.handlerInfo.store();
+ },
+
+ onCancel: function() {
+ // do nothing
+ },
+
+ remove: function() {
+ var list = document.getElementById("appList");
+ this._removed.push(list.selectedItem.app);
+ var index = list.selectedIndex;
+ list.removeItemAt(index);
+ if (list.getRowCount() == 0) {
+ // The list is now empty, make the bottom part disappear
+ document.getElementById("appDetails").hidden = true;
+ }
+ else {
+ // Select the item at the same index, if we removed the last
+ // item of the list, select the previous item
+ if (index == list.getRowCount())
+ --index;
+ list.selectedIndex = index;
+ }
+ },
+
+ onSelect: function() {
+ var list = document.getElementById("appList");
+ if (!list.selectedItem) {
+ document.getElementById("remove").disabled = true;
+ return;
+ }
+ document.getElementById("remove").disabled = false;
+ var app = list.selectedItem.app;
+ var address = "";
+ if (app instanceof Ci.nsILocalHandlerApp)
+ address = app.executable.path;
+ else if (app instanceof Ci.nsIWebHandlerApp)
+ address = app.uriTemplate;
+ else if (app instanceof Ci.nsIWebContentHandlerInfo)
+ address = app.uri;
+ document.getElementById("appLocation").value = address;
+ var bundle = document.getElementById("appManagerBundle");
+ var appType = app instanceof Ci.nsILocalHandlerApp ? "descriptionLocalApp"
+ : "descriptionWebApp";
+ document.getElementById("appType").value = bundle.getString(appType);
+ }
+};
diff --git a/browser/components/preferences/applicationManager.xul b/browser/components/preferences/applicationManager.xul
new file mode 100644
index 000000000..b5605c290
--- /dev/null
+++ b/browser/components/preferences/applicationManager.xul
@@ -0,0 +1,59 @@
+<?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/"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/applicationManager.dtd">
+
+<dialog id="appManager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ onload="gAppManagerDialog.init();"
+ ondialogaccept="gAppManagerDialog.onOK();"
+ ondialogcancel="gAppManagerDialog.onCancel();"
+ title="&appManager.title;"
+ style="&appManager.style;"
+ persist="screenX screenY">
+
+ <script type="application/javascript"
+ src="chrome://browser/content/utilityOverlay.js"/>
+ <script type="application/javascript"
+ src="chrome://browser/content/preferences/applicationManager.js"/>
+ <script type="application/javascript"
+ src="chrome://browser/content/preferences/applications.js"/>
+
+ <commandset id="appManagerCommandSet">
+ <command id="cmd_remove"
+ oncommand="gAppManagerDialog.remove();"
+ disabled="true"/>
+ </commandset>
+
+ <keyset id="appManagerKeyset">
+ <key id="delete" keycode="VK_DELETE" command="cmd_remove"/>
+ </keyset>
+
+ <stringbundleset id="appManagerBundleset">
+ <stringbundle id="appManagerBundle"
+ src="chrome://browser/locale/preferences/applicationManager.properties"/>
+ </stringbundleset>
+
+ <description id="appDescription"/>
+ <separator class="thin"/>
+ <hbox flex="1">
+ <listbox id="appList" onselect="gAppManagerDialog.onSelect();" flex="1"/>
+ <vbox>
+ <button id="remove"
+ label="&remove.label;"
+ accesskey="&remove.accesskey;"
+ command="cmd_remove"/>
+ <spacer flex="1"/>
+ </vbox>
+ </hbox>
+ <vbox id="appDetails">
+ <separator class="thin"/>
+ <label id="appType"/>
+ <textbox id="appLocation" readonly="true" class="plain"/>
+ </vbox>
+</dialog>
diff --git a/browser/components/preferences/applications.js b/browser/components/preferences/applications.js
new file mode 100644
index 000000000..3751ee732
--- /dev/null
+++ b/browser/components/preferences/applications.js
@@ -0,0 +1,1876 @@
+// 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/.
+
+//****************************************************************************//
+// Constants & Enumeration Values
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+
+const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
+const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
+const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
+
+const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";
+
+// Preferences that affect which entries to show in the list.
+const PREF_SHOW_PLUGINS_IN_LIST = "browser.download.show_plugins_in_list";
+const PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS =
+ "browser.download.hide_plugins_without_extensions";
+
+/*
+ * Preferences where we store handling information about the feed type.
+ *
+ * browser.feeds.handler
+ * - "bookmarks", "reader" (clarified further using the .default preference),
+ * or "ask" -- indicates the default handler being used to process feeds;
+ * "bookmarks" is obsolete; to specify that the handler is bookmarks,
+ * set browser.feeds.handler.default to "bookmarks";
+ *
+ * browser.feeds.handler.default
+ * - "bookmarks", "client" or "web" -- indicates the chosen feed reader used
+ * to display feeds, either transiently (i.e., when the "use as default"
+ * checkbox is unchecked, corresponds to when browser.feeds.handler=="ask")
+ * or more permanently (i.e., the item displayed in the dropdown in Feeds
+ * preferences)
+ *
+ * browser.feeds.handler.webservice
+ * - the URL of the currently selected web service used to read feeds
+ *
+ * browser.feeds.handlers.application
+ * - nsILocalFile, stores the current client-side feed reading app if one has
+ * been chosen
+ */
+const PREF_FEED_SELECTED_APP = "browser.feeds.handlers.application";
+const PREF_FEED_SELECTED_WEB = "browser.feeds.handlers.webservice";
+const PREF_FEED_SELECTED_ACTION = "browser.feeds.handler";
+const PREF_FEED_SELECTED_READER = "browser.feeds.handler.default";
+
+const PREF_VIDEO_FEED_SELECTED_APP = "browser.videoFeeds.handlers.application";
+const PREF_VIDEO_FEED_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
+const PREF_VIDEO_FEED_SELECTED_ACTION = "browser.videoFeeds.handler";
+const PREF_VIDEO_FEED_SELECTED_READER = "browser.videoFeeds.handler.default";
+
+const PREF_AUDIO_FEED_SELECTED_APP = "browser.audioFeeds.handlers.application";
+const PREF_AUDIO_FEED_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
+const PREF_AUDIO_FEED_SELECTED_ACTION = "browser.audioFeeds.handler";
+const PREF_AUDIO_FEED_SELECTED_READER = "browser.audioFeeds.handler.default";
+
+// The nsHandlerInfoAction enumeration values in nsIHandlerInfo identify
+// the actions the application can take with content of various types.
+// But since nsIHandlerInfo doesn't support plugins, there's no value
+// identifying the "use plugin" action, so we use this constant instead.
+const kActionUsePlugin = 5;
+
+/*
+#ifdef MOZ_WIDGET_GTK
+*/
+const ICON_URL_APP = "moz-icon://dummy.exe?size=16";
+/*
+#else
+*/
+const ICON_URL_APP = "chrome://browser/skin/preferences/application.png";
+/*
+#endif
+*/
+
+// For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL
+// was set by us to a custom handler icon and CSS should not try to override it.
+const APP_ICON_ATTR_NAME = "appHandlerIcon";
+
+//****************************************************************************//
+// Utilities
+
+function getFileDisplayName(file) {
+#ifdef XP_WIN
+ if (file instanceof Ci.nsILocalFileWin) {
+ try {
+ return file.getVersionInfoField("FileDescription");
+ } catch (e) {}
+ }
+#endif
+ return file.leafName;
+}
+
+function getLocalHandlerApp(aFile) {
+ var localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsILocalHandlerApp);
+ localHandlerApp.name = getFileDisplayName(aFile);
+ localHandlerApp.executable = aFile;
+
+ return localHandlerApp;
+}
+
+/**
+ * An enumeration of items in a JS array.
+ *
+ * FIXME: use ArrayConverter once it lands (bug 380839).
+ *
+ * @constructor
+ */
+function ArrayEnumerator(aItems) {
+ this._index = 0;
+ this._contents = aItems;
+}
+
+ArrayEnumerator.prototype = {
+ _index: 0,
+
+ hasMoreElements: function() {
+ return this._index < this._contents.length;
+ },
+
+ getNext: function() {
+ return this._contents[this._index++];
+ }
+};
+
+function isFeedType(t) {
+ return t == TYPE_MAYBE_FEED || t == TYPE_MAYBE_VIDEO_FEED || t == TYPE_MAYBE_AUDIO_FEED;
+}
+
+//****************************************************************************//
+// HandlerInfoWrapper
+
+/**
+ * This object wraps nsIHandlerInfo with some additional functionality
+ * the Applications prefpane needs to display and allow modification of
+ * the list of handled types.
+ *
+ * We create an instance of this wrapper for each entry we might display
+ * in the prefpane, and we compose the instances from various sources,
+ * including plugins and the handler service.
+ *
+ * We don't implement all the original nsIHandlerInfo functionality,
+ * just the stuff that the prefpane needs.
+ *
+ * In theory, all of the custom functionality in this wrapper should get
+ * pushed down into nsIHandlerInfo eventually.
+ */
+function HandlerInfoWrapper(aType, aHandlerInfo) {
+ this._type = aType;
+ this.wrappedHandlerInfo = aHandlerInfo;
+}
+
+HandlerInfoWrapper.prototype = {
+ // The wrapped nsIHandlerInfo object. In general, this object is private,
+ // but there are a couple cases where callers access it directly for things
+ // we haven't (yet?) implemented, so we make it a public property.
+ wrappedHandlerInfo: null,
+
+
+ //**************************************************************************//
+ // Convenience Utils
+
+ _handlerSvc: Cc["@mozilla.org/uriloader/handler-service;1"].
+ getService(Ci.nsIHandlerService),
+
+ _prefSvc: Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch),
+
+ _categoryMgr: Cc["@mozilla.org/categorymanager;1"].
+ getService(Ci.nsICategoryManager),
+
+ element: function(aID) {
+ return document.getElementById(aID);
+ },
+
+
+ //**************************************************************************//
+ // nsIHandlerInfo
+
+ // The MIME type or protocol scheme.
+ _type: null,
+ get type() {
+ return this._type;
+ },
+
+ get description() {
+ if (this.wrappedHandlerInfo.description)
+ return this.wrappedHandlerInfo.description;
+
+ if (this.primaryExtension) {
+ var extension = this.primaryExtension.toUpperCase();
+ return this.element("bundlePreferences").getFormattedString("fileEnding",
+ [extension]);
+ }
+
+ return this.type;
+ },
+
+ get preferredApplicationHandler() {
+ return this.wrappedHandlerInfo.preferredApplicationHandler;
+ },
+
+ set preferredApplicationHandler(aNewValue) {
+ this.wrappedHandlerInfo.preferredApplicationHandler = aNewValue;
+
+ // Make sure the preferred handler is in the set of possible handlers.
+ if (aNewValue)
+ this.addPossibleApplicationHandler(aNewValue)
+ },
+
+ get possibleApplicationHandlers() {
+ return this.wrappedHandlerInfo.possibleApplicationHandlers;
+ },
+
+ addPossibleApplicationHandler: function(aNewHandler) {
+ var possibleApps = this.possibleApplicationHandlers.enumerate();
+ while (possibleApps.hasMoreElements()) {
+ if (possibleApps.getNext().equals(aNewHandler))
+ return;
+ }
+ this.possibleApplicationHandlers.appendElement(aNewHandler, false);
+ },
+
+ removePossibleApplicationHandler: function(aHandler) {
+ var defaultApp = this.preferredApplicationHandler;
+ if (defaultApp && aHandler.equals(defaultApp)) {
+ // If the app we remove was the default app, we must make sure
+ // it won't be used anymore
+ this.alwaysAskBeforeHandling = true;
+ this.preferredApplicationHandler = null;
+ }
+
+ var handlers = this.possibleApplicationHandlers;
+ for (var i = 0; i < handlers.length; ++i) {
+ var handler = handlers.queryElementAt(i, Ci.nsIHandlerApp);
+ if (handler.equals(aHandler)) {
+ handlers.removeElementAt(i);
+ break;
+ }
+ }
+ },
+
+ get hasDefaultHandler() {
+ return this.wrappedHandlerInfo.hasDefaultHandler;
+ },
+
+ get defaultDescription() {
+ return this.wrappedHandlerInfo.defaultDescription;
+ },
+
+ // What to do with content of this type.
+ get preferredAction() {
+ // If we have an enabled plugin, then the action is to use that plugin.
+ if (this.pluginName && !this.isDisabledPluginType)
+ return kActionUsePlugin;
+
+ // If the action is to use a helper app, but we don't have a preferred
+ // handler app, then switch to using the system default, if any; otherwise
+ // fall back to saving to disk, which is the default action in nsMIMEInfo.
+ // Note: "save to disk" is an invalid value for protocol info objects,
+ // but the alwaysAskBeforeHandling getter will detect that situation
+ // and always return true in that case to override this invalid value.
+ if (this.wrappedHandlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
+ !gApplicationsPane.isValidHandlerApp(this.preferredApplicationHandler)) {
+ if (this.wrappedHandlerInfo.hasDefaultHandler)
+ return Ci.nsIHandlerInfo.useSystemDefault;
+ else
+ return Ci.nsIHandlerInfo.saveToDisk;
+ }
+
+ return this.wrappedHandlerInfo.preferredAction;
+ },
+
+ set preferredAction(aNewValue) {
+ // If the action is to use the plugin,
+ // we must set the preferred action to "save to disk".
+ // But only if it's not currently the preferred action.
+ if ((aNewValue == kActionUsePlugin) &&
+ (this.preferredAction != Ci.nsIHandlerInfo.saveToDisk)) {
+ aNewValue = Ci.nsIHandlerInfo.saveToDisk;
+ }
+
+ // We don't modify the preferred action if the new action is to use a plugin
+ // because handler info objects don't understand our custom "use plugin"
+ // value. Also, leaving it untouched means that we can automatically revert
+ // to the old setting if the user ever removes the plugin.
+
+ if (aNewValue != kActionUsePlugin)
+ this.wrappedHandlerInfo.preferredAction = aNewValue;
+ },
+
+ get alwaysAskBeforeHandling() {
+ // If this type is handled only by a plugin, we can't trust the value
+ // in the handler info object, since it'll be a default based on the absence
+ // of any user configuration, and the default in that case is to always ask,
+ // even though we never ask for content handled by a plugin, so special case
+ // plugin-handled types by returning false here.
+ if (this.pluginName && this.handledOnlyByPlugin)
+ return false;
+
+ // If this is a protocol type and the preferred action is "save to disk",
+ // which is invalid for such types, then return true here to override that
+ // action. This could happen when the preferred action is to use a helper
+ // app, but the preferredApplicationHandler is invalid, and there isn't
+ // a default handler, so the preferredAction getter returns save to disk
+ // instead.
+ if (!(this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) &&
+ this.preferredAction == Ci.nsIHandlerInfo.saveToDisk)
+ return true;
+
+ return this.wrappedHandlerInfo.alwaysAskBeforeHandling;
+ },
+
+ set alwaysAskBeforeHandling(aNewValue) {
+ this.wrappedHandlerInfo.alwaysAskBeforeHandling = aNewValue;
+ },
+
+
+ //**************************************************************************//
+ // nsIMIMEInfo
+
+ // The primary file extension associated with this type, if any.
+ //
+ // XXX Plugin objects contain an array of MimeType objects with "suffixes"
+ // properties; if this object has an associated plugin, shouldn't we check
+ // those properties for an extension?
+ get primaryExtension() {
+ try {
+ if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
+ this.wrappedHandlerInfo.primaryExtension)
+ return this.wrappedHandlerInfo.primaryExtension
+ } catch(ex) {}
+
+ return null;
+ },
+
+
+ //**************************************************************************//
+ // Plugin Handling
+
+ // A plugin that can handle this type, if any.
+ //
+ // Note: just because we have one doesn't mean it *will* handle the type.
+ // That depends on whether or not the type is in the list of types for which
+ // plugin handling is disabled.
+ plugin: null,
+
+ // Whether or not this type is only handled by a plugin or is also handled
+ // by some user-configured action as specified in the handler info object.
+ //
+ // Note: we can't just check if there's a handler info object for this type,
+ // because OS and user configuration is mixed up in the handler info object,
+ // so we always need to retrieve it for the OS info and can't tell whether
+ // it represents only OS-default information or user-configured information.
+ //
+ // FIXME: once handler info records are broken up into OS-provided records
+ // and user-configured records, stop using this boolean flag and simply
+ // check for the presence of a user-configured record to determine whether
+ // or not this type is only handled by a plugin. Filed as bug 395142.
+ handledOnlyByPlugin: undefined,
+
+ get isDisabledPluginType() {
+ return this._getDisabledPluginTypes().indexOf(this.type) != -1;
+ },
+
+ _getDisabledPluginTypes: function() {
+ var types = "";
+
+ if (this._prefSvc.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES))
+ types = this._prefSvc.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
+
+ // Only split if the string isn't empty so we don't end up with an array
+ // containing a single empty string.
+ if (types != "")
+ return types.split(",");
+
+ return [];
+ },
+
+ disablePluginType: function() {
+ var disabledPluginTypes = this._getDisabledPluginTypes();
+
+ if (disabledPluginTypes.indexOf(this.type) == -1)
+ disabledPluginTypes.push(this.type);
+
+ this._prefSvc.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
+ disabledPluginTypes.join(","));
+
+ // Update the category manager so existing browser windows update.
+ this._categoryMgr.deleteCategoryEntry("Goanna-Content-Viewers",
+ this.type,
+ false);
+ },
+
+ enablePluginType: function() {
+ var disabledPluginTypes = this._getDisabledPluginTypes();
+
+ var type = this.type;
+ disabledPluginTypes = disabledPluginTypes.filter(function(v) v != type);
+
+ this._prefSvc.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
+ disabledPluginTypes.join(","));
+
+ // Update the category manager so existing browser windows update.
+ this._categoryMgr.
+ addCategoryEntry("Goanna-Content-Viewers",
+ this.type,
+ "@mozilla.org/content/plugin/document-loader-factory;1",
+ false,
+ true);
+ },
+
+
+ //**************************************************************************//
+ // Storage
+
+ store: function() {
+ this._handlerSvc.store(this.wrappedHandlerInfo);
+ },
+
+
+ //**************************************************************************//
+ // Icons
+
+ get smallIcon() {
+ return this._getIcon(16);
+ },
+
+ _getIcon: function(aSize) {
+ if (this.primaryExtension)
+ return "moz-icon://goat." + this.primaryExtension + "?size=" + aSize;
+
+ if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo)
+ return "moz-icon://goat?size=" + aSize + "&contentType=" + this.type;
+
+ // FIXME: consider returning some generic icon when we can't get a URL for
+ // one (for example in the case of protocol schemes). Filed as bug 395141.
+ return null;
+ }
+
+};
+
+
+//****************************************************************************//
+// Feed Handler Info
+
+/**
+ * This object implements nsIHandlerInfo for the feed types. It's a separate
+ * object because we currently store handling information for the feed type
+ * in a set of preferences rather than the nsIHandlerService-managed datastore.
+ *
+ * This object inherits from HandlerInfoWrapper in order to get functionality
+ * that isn't special to the feed type.
+ *
+ * XXX Should we inherit from HandlerInfoWrapper? After all, we override
+ * most of that wrapper's properties and methods, and we have to dance around
+ * the fact that the wrapper expects to have a wrappedHandlerInfo, which we
+ * don't provide.
+ */
+
+function FeedHandlerInfo(aMIMEType) {
+ HandlerInfoWrapper.call(this, aMIMEType, null);
+}
+
+FeedHandlerInfo.prototype = {
+ __proto__: HandlerInfoWrapper.prototype,
+
+ //**************************************************************************//
+ // Convenience Utils
+
+ _converterSvc:
+ Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
+ getService(Ci.nsIWebContentConverterService),
+
+ _shellSvc:
+#ifdef HAVE_SHELL_SERVICE
+ getShellService(),
+#else
+ null,
+#endif
+
+
+ //**************************************************************************//
+ // nsIHandlerInfo
+
+ get description() {
+ return this.element("bundlePreferences").getString(this._appPrefLabel);
+ },
+
+ get preferredApplicationHandler() {
+ switch (this.element(this._prefSelectedReader).value) {
+ case "client":
+ var file = this.element(this._prefSelectedApp).value;
+ if (file)
+ return getLocalHandlerApp(file);
+
+ return null;
+
+ case "web":
+ var uri = this.element(this._prefSelectedWeb).value;
+ if (!uri)
+ return null;
+ return this._converterSvc.getWebContentHandlerByURI(this.type, uri);
+
+ case "bookmarks":
+ default:
+ // When the pref is set to bookmarks, we handle feeds internally,
+ // we don't forward them to a local or web handler app, so there is
+ // no preferred handler.
+ return null;
+ }
+ },
+
+ set preferredApplicationHandler(aNewValue) {
+ if (aNewValue instanceof Ci.nsILocalHandlerApp) {
+ this.element(this._prefSelectedApp).value = aNewValue.executable;
+ this.element(this._prefSelectedReader).value = "client";
+ }
+ else if (aNewValue instanceof Ci.nsIWebContentHandlerInfo) {
+ this.element(this._prefSelectedWeb).value = aNewValue.uri;
+ this.element(this._prefSelectedReader).value = "web";
+ // Make the web handler be the new "auto handler" for feeds.
+ // Note: we don't have to unregister the auto handler when the user picks
+ // a non-web handler (local app, Live Bookmarks, etc.) because the service
+ // only uses the "auto handler" when the selected reader is a web handler.
+ // We also don't have to unregister it when the user turns on "always ask"
+ // (i.e. preview in browser), since that also overrides the auto handler.
+ this._converterSvc.setAutoHandler(this.type, aNewValue);
+ }
+ },
+
+ _possibleApplicationHandlers: null,
+
+ get possibleApplicationHandlers() {
+ if (this._possibleApplicationHandlers)
+ return this._possibleApplicationHandlers;
+
+ // A minimal implementation of nsIMutableArray. It only supports the two
+ // methods its callers invoke, namely appendElement and nsIArray::enumerate.
+ this._possibleApplicationHandlers = {
+ _inner: [],
+ _removed: [],
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIMutableArray) ||
+ aIID.equals(Ci.nsIArray) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ get length() {
+ return this._inner.length;
+ },
+
+ enumerate: function() {
+ return new ArrayEnumerator(this._inner);
+ },
+
+ appendElement: function(aHandlerApp, aWeak) {
+ this._inner.push(aHandlerApp);
+ },
+
+ removeElementAt: function(aIndex) {
+ this._removed.push(this._inner[aIndex]);
+ this._inner.splice(aIndex, 1);
+ },
+
+ queryElementAt: function(aIndex, aInterface) {
+ return this._inner[aIndex].QueryInterface(aInterface);
+ }
+ };
+
+ // Add the selected local app if it's different from the OS default handler.
+ // Unlike for other types, we can store only one local app at a time for the
+ // feed type, since we store it in a preference that historically stores
+ // only a single path. But we display all the local apps the user chooses
+ // while the prefpane is open, only dropping the list when the user closes
+ // the prefpane, for maximum usability and consistency with other types.
+ var preferredAppFile = this.element(this._prefSelectedApp).value;
+ if (preferredAppFile) {
+ let preferredApp = getLocalHandlerApp(preferredAppFile);
+ let defaultApp = this._defaultApplicationHandler;
+ if (!defaultApp || !defaultApp.equals(preferredApp))
+ this._possibleApplicationHandlers.appendElement(preferredApp, false);
+ }
+
+ // Add the registered web handlers. There can be any number of these.
+ var webHandlers = this._converterSvc.getContentHandlers(this.type);
+ for each (let webHandler in webHandlers)
+ this._possibleApplicationHandlers.appendElement(webHandler, false);
+
+ return this._possibleApplicationHandlers;
+ },
+
+ __defaultApplicationHandler: undefined,
+ get _defaultApplicationHandler() {
+ if (typeof this.__defaultApplicationHandler != "undefined")
+ return this.__defaultApplicationHandler;
+
+ var defaultFeedReader = null;
+#ifdef HAVE_SHELL_SERVICE
+ try {
+ defaultFeedReader = this._shellSvc.defaultFeedReader;
+ }
+ catch(ex) {
+ // no default reader or _shellSvc is null
+ }
+#endif
+
+ if (defaultFeedReader) {
+ let handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsIHandlerApp);
+ handlerApp.name = getFileDisplayName(defaultFeedReader);
+ handlerApp.QueryInterface(Ci.nsILocalHandlerApp);
+ handlerApp.executable = defaultFeedReader;
+
+ this.__defaultApplicationHandler = handlerApp;
+ }
+ else {
+ this.__defaultApplicationHandler = null;
+ }
+
+ return this.__defaultApplicationHandler;
+ },
+
+ get hasDefaultHandler() {
+#ifdef HAVE_SHELL_SERVICE
+ try {
+ if (this._shellSvc.defaultFeedReader)
+ return true;
+ }
+ catch(ex) {
+ // no default reader or _shellSvc is null
+ }
+#endif
+
+ return false;
+ },
+
+ get defaultDescription() {
+ if (this.hasDefaultHandler)
+ return this._defaultApplicationHandler.name;
+
+ // Should we instead return null?
+ return "";
+ },
+
+ // What to do with content of this type.
+ get preferredAction() {
+ switch (this.element(this._prefSelectedAction).value) {
+
+ case "bookmarks":
+ return Ci.nsIHandlerInfo.handleInternally;
+
+ case "reader": {
+ let preferredApp = this.preferredApplicationHandler;
+ let defaultApp = this._defaultApplicationHandler;
+
+ // If we have a valid preferred app, return useSystemDefault if it's
+ // the default app; otherwise return useHelperApp.
+ if (gApplicationsPane.isValidHandlerApp(preferredApp)) {
+ if (defaultApp && defaultApp.equals(preferredApp))
+ return Ci.nsIHandlerInfo.useSystemDefault;
+
+ return Ci.nsIHandlerInfo.useHelperApp;
+ }
+
+ // The pref is set to "reader", but we don't have a valid preferred app.
+ // What do we do now? Not sure this is the best option (perhaps we
+ // should direct the user to the default app, if any), but for now let's
+ // direct the user to live bookmarks.
+ return Ci.nsIHandlerInfo.handleInternally;
+ }
+
+ // If the action is "ask", then alwaysAskBeforeHandling will override
+ // the action, so it doesn't matter what we say it is, it just has to be
+ // something that doesn't cause the controller to hide the type.
+ case "ask":
+ default:
+ return Ci.nsIHandlerInfo.handleInternally;
+ }
+ },
+
+ set preferredAction(aNewValue) {
+ switch (aNewValue) {
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ this.element(this._prefSelectedReader).value = "bookmarks";
+ break;
+
+ case Ci.nsIHandlerInfo.useHelperApp:
+ this.element(this._prefSelectedAction).value = "reader";
+ // The controller has already set preferredApplicationHandler
+ // to the new helper app.
+ break;
+
+ case Ci.nsIHandlerInfo.useSystemDefault:
+ this.element(this._prefSelectedAction).value = "reader";
+ this.preferredApplicationHandler = this._defaultApplicationHandler;
+ break;
+ }
+ },
+
+ get alwaysAskBeforeHandling() {
+ return this.element(this._prefSelectedAction).value == "ask";
+ },
+
+ set alwaysAskBeforeHandling(aNewValue) {
+ if (aNewValue == true)
+ this.element(this._prefSelectedAction).value = "ask";
+ else
+ this.element(this._prefSelectedAction).value = "reader";
+ },
+
+ // Whether or not we are currently storing the action selected by the user.
+ // We use this to suppress notification-triggered updates to the list when
+ // we make changes that may spawn such updates, specifically when we change
+ // the action for the feed type, which results in feed preference updates,
+ // which spawn "pref changed" notifications that would otherwise cause us
+ // to rebuild the view unnecessarily.
+ _storingAction: false,
+
+
+ //**************************************************************************//
+ // nsIMIMEInfo
+
+ get primaryExtension() {
+ return "xml";
+ },
+
+
+ //**************************************************************************//
+ // Storage
+
+ // Changes to the preferred action and handler take effect immediately
+ // (we write them out to the preferences right as they happen),
+ // so we when the controller calls store() after modifying the handlers,
+ // the only thing we need to store is the removal of possible handlers
+ // XXX Should we hold off on making the changes until this method gets called?
+ store: function() {
+ for each (let app in this._possibleApplicationHandlers._removed) {
+ if (app instanceof Ci.nsILocalHandlerApp) {
+ let pref = this.element(PREF_FEED_SELECTED_APP);
+ var preferredAppFile = pref.value;
+ if (preferredAppFile) {
+ let preferredApp = getLocalHandlerApp(preferredAppFile);
+ if (app.equals(preferredApp))
+ pref.reset();
+ }
+ }
+ else {
+ app.QueryInterface(Ci.nsIWebContentHandlerInfo);
+ this._converterSvc.removeContentHandler(app.contentType, app.uri);
+ }
+ }
+ this._possibleApplicationHandlers._removed = [];
+ },
+
+
+ //**************************************************************************//
+ // Icons
+
+ get smallIcon() {
+ return this._smallIcon;
+ }
+
+};
+
+var feedHandlerInfo = {
+ __proto__: new FeedHandlerInfo(TYPE_MAYBE_FEED),
+ _prefSelectedApp: PREF_FEED_SELECTED_APP,
+ _prefSelectedWeb: PREF_FEED_SELECTED_WEB,
+ _prefSelectedAction: PREF_FEED_SELECTED_ACTION,
+ _prefSelectedReader: PREF_FEED_SELECTED_READER,
+ _smallIcon: "chrome://browser/skin/feeds/feedIcon16.png",
+ _appPrefLabel: "webFeed"
+}
+
+var videoFeedHandlerInfo = {
+ __proto__: new FeedHandlerInfo(TYPE_MAYBE_VIDEO_FEED),
+ _prefSelectedApp: PREF_VIDEO_FEED_SELECTED_APP,
+ _prefSelectedWeb: PREF_VIDEO_FEED_SELECTED_WEB,
+ _prefSelectedAction: PREF_VIDEO_FEED_SELECTED_ACTION,
+ _prefSelectedReader: PREF_VIDEO_FEED_SELECTED_READER,
+ _smallIcon: "chrome://browser/skin/feeds/videoFeedIcon16.png",
+ _appPrefLabel: "videoPodcastFeed"
+}
+
+var audioFeedHandlerInfo = {
+ __proto__: new FeedHandlerInfo(TYPE_MAYBE_AUDIO_FEED),
+ _prefSelectedApp: PREF_AUDIO_FEED_SELECTED_APP,
+ _prefSelectedWeb: PREF_AUDIO_FEED_SELECTED_WEB,
+ _prefSelectedAction: PREF_AUDIO_FEED_SELECTED_ACTION,
+ _prefSelectedReader: PREF_AUDIO_FEED_SELECTED_READER,
+ _smallIcon: "chrome://browser/skin/feeds/audioFeedIcon16.png",
+ _appPrefLabel: "audioPodcastFeed"
+}
+
+/**
+ * InternalHandlerInfoWrapper provides a basic mechanism to create an internal
+ * mime type handler that can be enabled/disabled in the applications preference
+ * menu.
+ */
+function InternalHandlerInfoWrapper(aMIMEType) {
+ var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+ var handlerInfo = mimeSvc.getFromTypeAndExtension(aMIMEType, null);
+
+ HandlerInfoWrapper.call(this, aMIMEType, handlerInfo);
+}
+
+InternalHandlerInfoWrapper.prototype = {
+ __proto__: HandlerInfoWrapper.prototype,
+
+ // Override store so we so we can notify any code listening for registration
+ // or unregistration of this handler.
+ store: function() {
+ HandlerInfoWrapper.prototype.store.call(this);
+ Services.obs.notifyObservers(null, this._handlerChanged, null);
+ },
+
+ get enabled() {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ get description() {
+ return this.element("bundlePreferences").getString(this._appPrefLabel);
+ }
+};
+
+//****************************************************************************//
+// Prefpane Controller
+
+var gApplicationsPane = {
+ // The set of types the app knows how to handle. A hash of HandlerInfoWrapper
+ // objects, indexed by type.
+ _handledTypes: {},
+
+ // The list of types we can show, sorted by the sort column/direction.
+ // An array of HandlerInfoWrapper objects. We build this list when we first
+ // load the data and then rebuild it when users change a pref that affects
+ // what types we can show or change the sort column/direction.
+ // Note: this isn't necessarily the list of types we *will* show; if the user
+ // provides a filter string, we'll only show the subset of types in this list
+ // that match that string.
+ _visibleTypes: [],
+
+ // A count of the number of times each visible type description appears.
+ // We use these counts to determine whether or not to annotate descriptions
+ // with their types to distinguish duplicate descriptions from each other.
+ // A hash of integer counts, indexed by string description.
+ _visibleTypeDescriptionCount: {},
+
+
+ //**************************************************************************//
+ // Convenience & Performance Shortcuts
+
+ // These get defined by init().
+ _brandShortName : null,
+ _prefsBundle : null,
+ _list : null,
+ _filter : null,
+
+ _prefSvc : Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch),
+
+ _mimeSvc : Cc["@mozilla.org/mime;1"].
+ getService(Ci.nsIMIMEService),
+
+ _helperAppSvc : Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
+ getService(Ci.nsIExternalHelperAppService),
+
+ _handlerSvc : Cc["@mozilla.org/uriloader/handler-service;1"].
+ getService(Ci.nsIHandlerService),
+
+ _ioSvc : Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService),
+
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ init: function() {
+ // Initialize shortcuts to some commonly accessed elements & values.
+ this._brandShortName =
+ document.getElementById("bundleBrand").getString("brandShortName");
+ this._prefsBundle = document.getElementById("bundlePreferences");
+ this._list = document.getElementById("handlersView");
+ this._filter = document.getElementById("filter");
+
+ // Observe preferences that influence what we display so we can rebuild
+ // the view when they change.
+ this._prefSvc.addObserver(PREF_SHOW_PLUGINS_IN_LIST, this, false);
+ this._prefSvc.addObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this, false);
+ this._prefSvc.addObserver(PREF_FEED_SELECTED_APP, this, false);
+ this._prefSvc.addObserver(PREF_FEED_SELECTED_WEB, this, false);
+ this._prefSvc.addObserver(PREF_FEED_SELECTED_ACTION, this, false);
+ this._prefSvc.addObserver(PREF_FEED_SELECTED_READER, this, false);
+
+ this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_APP, this, false);
+ this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_WEB, this, false);
+ this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this, false);
+ this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_READER, this, false);
+
+ this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_APP, this, false);
+ this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_WEB, this, false);
+ this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this, false);
+ this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_READER, this, false);
+
+
+ // Listen for window unload so we can remove our preference observers.
+ window.addEventListener("unload", this, false);
+
+ // Figure out how we should be sorting the list. We persist sort settings
+ // across sessions, so we can't assume the default sort column/direction.
+ // XXX should we be using the XUL sort service instead?
+ if (document.getElementById("actionColumn").hasAttribute("sortDirection")) {
+ this._sortColumn = document.getElementById("actionColumn");
+ // The typeColumn element always has a sortDirection attribute,
+ // either because it was persisted or because the default value
+ // from the xul file was used. If we are sorting on the other
+ // column, we should remove it.
+ document.getElementById("typeColumn").removeAttribute("sortDirection");
+ }
+ else
+ this._sortColumn = document.getElementById("typeColumn");
+
+ // Load the data and build the list of handlers.
+ // By doing this in a timeout, we let the preferences dialog resize itself
+ // to an appropriate size before we add a bunch of items to the list.
+ // Otherwise, if there are many items, and the Applications prefpane
+ // is the one that gets displayed when the user first opens the dialog,
+ // the dialog might stretch too much in an attempt to fit them all in.
+ // XXX Shouldn't we perhaps just set a max-height on the richlistbox?
+ var _delayedPaneLoad = function(self) {
+ self._loadData();
+ self._rebuildVisibleTypes();
+ self._sortVisibleTypes();
+ self._rebuildView();
+
+ // Notify observers that the UI is now ready
+ Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService).
+ notifyObservers(window, "app-handler-pane-loaded", null);
+ }
+ setTimeout(_delayedPaneLoad, 0, this);
+ },
+
+ destroy: function() {
+ window.removeEventListener("unload", this, false);
+ this._prefSvc.removeObserver(PREF_SHOW_PLUGINS_IN_LIST, this);
+ this._prefSvc.removeObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this);
+ this._prefSvc.removeObserver(PREF_FEED_SELECTED_APP, this);
+ this._prefSvc.removeObserver(PREF_FEED_SELECTED_WEB, this);
+ this._prefSvc.removeObserver(PREF_FEED_SELECTED_ACTION, this);
+ this._prefSvc.removeObserver(PREF_FEED_SELECTED_READER, this);
+
+ this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_APP, this);
+ this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_WEB, this);
+ this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this);
+ this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_READER, this);
+
+ this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_APP, this);
+ this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_WEB, this);
+ this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this);
+ this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_READER, this);
+ },
+
+
+ //**************************************************************************//
+ // nsISupports
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIObserver) ||
+ aIID.equals(Ci.nsIDOMEventListener ||
+ aIID.equals(Ci.nsISupports)))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ //**************************************************************************//
+ // nsIObserver
+
+ observe: function (aSubject, aTopic, aData) {
+ // Rebuild the list when there are changes to preferences that influence
+ // whether or not to show certain entries in the list.
+ if (aTopic == "nsPref:changed" && !this._storingAction) {
+ // These two prefs alter the list of visible types, so we have to rebuild
+ // that list when they change.
+ if (aData == PREF_SHOW_PLUGINS_IN_LIST ||
+ aData == PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS) {
+ this._rebuildVisibleTypes();
+ this._sortVisibleTypes();
+ }
+
+ // All the prefs we observe can affect what we display, so we rebuild
+ // the view when any of them changes.
+ this._rebuildView();
+ }
+ },
+
+
+ //**************************************************************************//
+ // nsIDOMEventListener
+
+ handleEvent: function(aEvent) {
+ if (aEvent.type == "unload") {
+ this.destroy();
+ }
+ },
+
+
+ //**************************************************************************//
+ // Composed Model Construction
+
+ _loadData: function() {
+ this._loadFeedHandler();
+ this._loadPluginHandlers();
+ this._loadApplicationHandlers();
+ },
+
+ _loadFeedHandler: function() {
+ this._handledTypes[TYPE_MAYBE_FEED] = feedHandlerInfo;
+ feedHandlerInfo.handledOnlyByPlugin = false;
+
+ this._handledTypes[TYPE_MAYBE_VIDEO_FEED] = videoFeedHandlerInfo;
+ videoFeedHandlerInfo.handledOnlyByPlugin = false;
+
+ this._handledTypes[TYPE_MAYBE_AUDIO_FEED] = audioFeedHandlerInfo;
+ audioFeedHandlerInfo.handledOnlyByPlugin = false;
+ },
+
+ /**
+ * Load the set of handlers defined by plugins.
+ *
+ * Note: if there's more than one plugin for a given MIME type, we assume
+ * the last one is the one that the application will use. That may not be
+ * correct, but it's how we've been doing it for years.
+ *
+ * Perhaps we should instead query navigator.mimeTypes for the set of types
+ * supported by the application and then get the plugin from each MIME type's
+ * enabledPlugin property. But if there's a plugin for a type, we need
+ * to know about it even if it isn't enabled, since we're going to give
+ * the user an option to enable it.
+ *
+ * Also note that enabledPlugin does not get updated when
+ * plugin.disable_full_page_plugin_for_types changes, so even if we could use
+ * enabledPlugin to get the plugin that would be used, we'd still need to
+ * check the pref ourselves to find out if it's enabled.
+ */
+ _loadPluginHandlers: function() {
+ "use strict";
+
+ let mimeTypes = navigator.mimeTypes;
+
+ for (let mimeType of mimeTypes) {
+ let handlerInfoWrapper;
+ if (mimeType.type in this._handledTypes) {
+ handlerInfoWrapper = this._handledTypes[mimeType.type];
+ } else {
+ let wrappedHandlerInfo =
+ this._mimeSvc.getFromTypeAndExtension(mimeType.type, null);
+ handlerInfoWrapper = new HandlerInfoWrapper(mimeType.type, wrappedHandlerInfo);
+ handlerInfoWrapper.handledOnlyByPlugin = true;
+ this._handledTypes[mimeType.type] = handlerInfoWrapper;
+ }
+ handlerInfoWrapper.pluginName = mimeType.enabledPlugin.name;
+ }
+ },
+
+ /**
+ * Load the set of handlers defined by the application datastore.
+ */
+ _loadApplicationHandlers: function() {
+ var wrappedHandlerInfos = this._handlerSvc.enumerate();
+ while (wrappedHandlerInfos.hasMoreElements()) {
+ let wrappedHandlerInfo =
+ wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+ let type = wrappedHandlerInfo.type;
+
+ let handlerInfoWrapper;
+ if (type in this._handledTypes)
+ handlerInfoWrapper = this._handledTypes[type];
+ else {
+ handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo);
+ this._handledTypes[type] = handlerInfoWrapper;
+ }
+
+ handlerInfoWrapper.handledOnlyByPlugin = false;
+ }
+ },
+
+
+ //**************************************************************************//
+ // View Construction
+
+ _rebuildVisibleTypes: function() {
+ // Reset the list of visible types and the visible type description counts.
+ this._visibleTypes = [];
+ this._visibleTypeDescriptionCount = {};
+
+ // Get the preferences that help determine what types to show.
+ var showPlugins = this._prefSvc.getBoolPref(PREF_SHOW_PLUGINS_IN_LIST);
+ var hidePluginsWithoutExtensions =
+ this._prefSvc.getBoolPref(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS);
+
+ for (let type in this._handledTypes) {
+ let handlerInfo = this._handledTypes[type];
+
+ // Hide plugins without associated extensions if so prefed so we don't
+ // show a whole bunch of obscure types handled by plugins on Mac.
+ // Note: though protocol types don't have extensions, we still show them;
+ // the pref is only meant to be applied to MIME types, since plugins are
+ // only associated with MIME types.
+ // FIXME: should we also check the "suffixes" property of the plugin?
+ // Filed as bug 395135.
+ if (hidePluginsWithoutExtensions && handlerInfo.handledOnlyByPlugin &&
+ handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
+ !handlerInfo.primaryExtension)
+ continue;
+
+ // Hide types handled only by plugins if so prefed.
+ if (handlerInfo.handledOnlyByPlugin && !showPlugins)
+ continue;
+
+ // We couldn't find any reason to exclude the type, so include it.
+ this._visibleTypes.push(handlerInfo);
+
+ if (handlerInfo.description in this._visibleTypeDescriptionCount)
+ this._visibleTypeDescriptionCount[handlerInfo.description]++;
+ else
+ this._visibleTypeDescriptionCount[handlerInfo.description] = 1;
+ }
+ },
+
+ _rebuildView: function() {
+ // Clear the list of entries.
+ while (this._list.childNodes.length > 1)
+ this._list.removeChild(this._list.lastChild);
+
+ var visibleTypes = this._visibleTypes;
+
+ // If the user is filtering the list, then only show matching types.
+ if (this._filter.value)
+ visibleTypes = visibleTypes.filter(this._matchesFilter, this);
+
+ for each (let visibleType in visibleTypes) {
+ let item = document.createElement("richlistitem");
+ item.setAttribute("type", visibleType.type);
+ item.setAttribute("typeDescription", this._describeType(visibleType));
+ if (visibleType.smallIcon)
+ item.setAttribute("typeIcon", visibleType.smallIcon);
+ item.setAttribute("actionDescription",
+ this._describePreferredAction(visibleType));
+
+ if (!this._setIconClassForPreferredAction(visibleType, item)) {
+ item.setAttribute("actionIcon",
+ this._getIconURLForPreferredAction(visibleType));
+ }
+
+ this._list.appendChild(item);
+ }
+
+ this._selectLastSelectedType();
+ },
+
+ _matchesFilter: function(aType) {
+ var filterValue = this._filter.value.toLowerCase();
+ return this._describeType(aType).toLowerCase().indexOf(filterValue) != -1 ||
+ this._describePreferredAction(aType).toLowerCase().indexOf(filterValue) != -1;
+ },
+
+ /**
+ * Describe, in a human-readable fashion, the type represented by the given
+ * handler info object. Normally this is just the description provided by
+ * the info object, but if more than one object presents the same description,
+ * then we annotate the duplicate descriptions with the type itself to help
+ * users distinguish between those types.
+ *
+ * @param aHandlerInfo {nsIHandlerInfo} the type being described
+ * @returns {string} a description of the type
+ */
+ _describeType: function(aHandlerInfo) {
+ if (this._visibleTypeDescriptionCount[aHandlerInfo.description] > 1)
+ return this._prefsBundle.getFormattedString("typeDescriptionWithType",
+ [aHandlerInfo.description,
+ aHandlerInfo.type]);
+
+ return aHandlerInfo.description;
+ },
+
+ /**
+ * Describe, in a human-readable fashion, the preferred action to take on
+ * the type represented by the given handler info object.
+ *
+ * XXX Should this be part of the HandlerInfoWrapper interface? It would
+ * violate the separation of model and view, but it might make more sense
+ * nonetheless (f.e. it would make sortTypes easier).
+ *
+ * @param aHandlerInfo {nsIHandlerInfo} the type whose preferred action
+ * is being described
+ * @returns {string} a description of the action
+ */
+ _describePreferredAction: function(aHandlerInfo) {
+ // alwaysAskBeforeHandling overrides the preferred action, so if that flag
+ // is set, then describe that behavior instead. For most types, this is
+ // the "alwaysAsk" string, but for the feed type we show something special.
+ if (aHandlerInfo.alwaysAskBeforeHandling) {
+ if (isFeedType(aHandlerInfo.type))
+ return this._prefsBundle.getFormattedString("previewInApp",
+ [this._brandShortName]);
+ else
+ return this._prefsBundle.getString("alwaysAsk");
+ }
+
+ switch (aHandlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.saveToDisk:
+ return this._prefsBundle.getString("saveFile");
+
+ case Ci.nsIHandlerInfo.useHelperApp:
+ var preferredApp = aHandlerInfo.preferredApplicationHandler;
+ var name;
+ if (preferredApp instanceof Ci.nsILocalHandlerApp)
+ name = getFileDisplayName(preferredApp.executable);
+ else
+ name = preferredApp.name;
+ return this._prefsBundle.getFormattedString("useApp", [name]);
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ // For the feed type, handleInternally means live bookmarks.
+ if (isFeedType(aHandlerInfo.type)) {
+ return this._prefsBundle.getFormattedString("addLiveBookmarksInApp",
+ [this._brandShortName]);
+ }
+
+ if (aHandlerInfo instanceof InternalHandlerInfoWrapper) {
+ return this._prefsBundle.getFormattedString("previewInApp",
+ [this._brandShortName]);
+ }
+
+ // For other types, handleInternally looks like either useHelperApp
+ // or useSystemDefault depending on whether or not there's a preferred
+ // handler app.
+ if (this.isValidHandlerApp(aHandlerInfo.preferredApplicationHandler))
+ return aHandlerInfo.preferredApplicationHandler.name;
+
+ return aHandlerInfo.defaultDescription;
+
+ // XXX Why don't we say the app will handle the type internally?
+ // Is it because the app can't actually do that? But if that's true,
+ // then why would a preferredAction ever get set to this value
+ // in the first place?
+
+ case Ci.nsIHandlerInfo.useSystemDefault:
+ return this._prefsBundle.getFormattedString("useDefault",
+ [aHandlerInfo.defaultDescription]);
+
+ case kActionUsePlugin:
+ return this._prefsBundle.getFormattedString("usePluginIn",
+ [aHandlerInfo.pluginName,
+ this._brandShortName]);
+ }
+ },
+
+ _selectLastSelectedType: function() {
+ // If the list is disabled by the pref.downloads.disable_button.edit_actions
+ // preference being locked, then don't select the type, as that would cause
+ // it to appear selected, with a different background and an actions menu
+ // that makes it seem like you can choose an action for the type.
+ if (this._list.disabled)
+ return;
+
+ var lastSelectedType = this._list.getAttribute("lastSelectedType");
+ if (!lastSelectedType)
+ return;
+
+ var item = this._list.getElementsByAttribute("type", lastSelectedType)[0];
+ if (!item)
+ return;
+
+ this._list.selectedItem = item;
+ },
+
+ /**
+ * Whether or not the given handler app is valid.
+ *
+ * @param aHandlerApp {nsIHandlerApp} the handler app in question
+ *
+ * @returns {boolean} whether or not it's valid
+ */
+ isValidHandlerApp: function(aHandlerApp) {
+ if (!aHandlerApp)
+ return false;
+
+ if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
+ return this._isValidHandlerExecutable(aHandlerApp.executable);
+
+ if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
+ return aHandlerApp.uriTemplate;
+
+ if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
+ return aHandlerApp.uri;
+
+ return false;
+ },
+
+ _isValidHandlerExecutable: function(aExecutable) {
+ return aExecutable &&
+ aExecutable.exists() &&
+ aExecutable.isExecutable() &&
+// XXXben - we need to compare this with the running instance executable
+// just don't know how to do that via script...
+// XXXmano TBD: can probably add this to nsIShellService
+#ifdef XP_WIN
+#expand aExecutable.leafName != "__MOZ_APP_NAME__.exe";
+#else
+#expand aExecutable.leafName != "__MOZ_APP_NAME__-bin";
+#endif
+ },
+
+ /**
+ * Rebuild the actions menu for the selected entry. Gets called by
+ * the richlistitem constructor when an entry in the list gets selected.
+ */
+ rebuildActionsMenu: function() {
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+ var menu =
+ document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+ var menuPopup = menu.menupopup;
+
+ // Clear out existing items.
+ while (menuPopup.hasChildNodes())
+ menuPopup.removeChild(menuPopup.lastChild);
+
+ // Add the "Preview in Firefox" option for optional internal handlers.
+ if (handlerInfo instanceof InternalHandlerInfoWrapper) {
+ var internalMenuItem = document.createElement("menuitem");
+ internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally);
+ let label = this._prefsBundle.getFormattedString("previewInApp",
+ [this._brandShortName]);
+ internalMenuItem.setAttribute("label", label);
+ internalMenuItem.setAttribute("tooltiptext", label);
+ internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask");
+ menuPopup.appendChild(internalMenuItem);
+ }
+
+ {
+ var askMenuItem = document.createElement("menuitem");
+ askMenuItem.setAttribute("action", Ci.nsIHandlerInfo.alwaysAsk);
+ let label;
+ if (isFeedType(handlerInfo.type))
+ label = this._prefsBundle.getFormattedString("previewInApp",
+ [this._brandShortName]);
+ else
+ label = this._prefsBundle.getString("alwaysAsk");
+ askMenuItem.setAttribute("label", label);
+ askMenuItem.setAttribute("tooltiptext", label);
+ askMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask");
+ menuPopup.appendChild(askMenuItem);
+ }
+
+ // Create a menu item for saving to disk.
+ // Note: this option isn't available to protocol types, since we don't know
+ // what it means to save a URL having a certain scheme to disk, nor is it
+ // available to feeds, since the feed code doesn't implement the capability.
+ if ((handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) &&
+ !isFeedType(handlerInfo.type)) {
+ var saveMenuItem = document.createElement("menuitem");
+ saveMenuItem.setAttribute("action", Ci.nsIHandlerInfo.saveToDisk);
+ let label = this._prefsBundle.getString("saveFile");
+ saveMenuItem.setAttribute("label", label);
+ saveMenuItem.setAttribute("tooltiptext", label);
+ saveMenuItem.setAttribute(APP_ICON_ATTR_NAME, "save");
+ menuPopup.appendChild(saveMenuItem);
+ }
+
+ // If this is the feed type, add a Live Bookmarks item.
+ if (isFeedType(handlerInfo.type)) {
+ var internalMenuItem = document.createElement("menuitem");
+ internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally);
+ let label = this._prefsBundle.getFormattedString("addLiveBookmarksInApp",
+ [this._brandShortName]);
+ internalMenuItem.setAttribute("label", label);
+ internalMenuItem.setAttribute("tooltiptext", label);
+ internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "feed");
+ menuPopup.appendChild(internalMenuItem);
+ }
+
+ // Add a separator to distinguish these items from the helper app items
+ // that follow them.
+ let menuItem = document.createElement("menuseparator");
+ menuPopup.appendChild(menuItem);
+
+ // Create a menu item for the OS default application, if any.
+ if (handlerInfo.hasDefaultHandler) {
+ var defaultMenuItem = document.createElement("menuitem");
+ defaultMenuItem.setAttribute("action", Ci.nsIHandlerInfo.useSystemDefault);
+ let label = this._prefsBundle.getFormattedString("useDefault",
+ [handlerInfo.defaultDescription]);
+ defaultMenuItem.setAttribute("label", label);
+ defaultMenuItem.setAttribute("tooltiptext", handlerInfo.defaultDescription);
+ defaultMenuItem.setAttribute("image", this._getIconURLForSystemDefault(handlerInfo));
+
+ menuPopup.appendChild(defaultMenuItem);
+ }
+
+ // Create menu items for possible handlers.
+ let preferredApp = handlerInfo.preferredApplicationHandler;
+ let possibleApps = handlerInfo.possibleApplicationHandlers.enumerate();
+ var possibleAppMenuItems = [];
+ while (possibleApps.hasMoreElements()) {
+ let possibleApp = possibleApps.getNext();
+ if (!this.isValidHandlerApp(possibleApp))
+ continue;
+
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
+ let label;
+ if (possibleApp instanceof Ci.nsILocalHandlerApp)
+ label = getFileDisplayName(possibleApp.executable);
+ else
+ label = possibleApp.name;
+ label = this._prefsBundle.getFormattedString("useApp", [label]);
+ menuItem.setAttribute("label", label);
+ menuItem.setAttribute("tooltiptext", label);
+ menuItem.setAttribute("image", this._getIconURLForHandlerApp(possibleApp));
+
+ // Attach the handler app object to the menu item so we can use it
+ // to make changes to the datastore when the user selects the item.
+ menuItem.handlerApp = possibleApp;
+
+ menuPopup.appendChild(menuItem);
+ possibleAppMenuItems.push(menuItem);
+ }
+
+ // Create a menu item for the plugin.
+ if (handlerInfo.pluginName) {
+ var pluginMenuItem = document.createElement("menuitem");
+ pluginMenuItem.setAttribute("action", kActionUsePlugin);
+ let label = this._prefsBundle.getFormattedString("usePluginIn",
+ [handlerInfo.pluginName,
+ this._brandShortName]);
+ pluginMenuItem.setAttribute("label", label);
+ pluginMenuItem.setAttribute("tooltiptext", label);
+ pluginMenuItem.setAttribute(APP_ICON_ATTR_NAME, "plugin");
+ menuPopup.appendChild(pluginMenuItem);
+ }
+
+ // Create a menu item for selecting a local application.
+#ifdef XP_WIN
+ // On Windows, selecting an application to open another application
+ // would be meaningless so we special case executables.
+ var executableType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService)
+ .getTypeFromExtension("exe");
+ if (handlerInfo.type != executableType)
+#endif
+ {
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("oncommand", "gApplicationsPane.chooseApp(event)");
+ let label = this._prefsBundle.getString("useOtherApp");
+ menuItem.setAttribute("label", label);
+ menuItem.setAttribute("tooltiptext", label);
+ menuPopup.appendChild(menuItem);
+ }
+
+ // Create a menu item for managing applications.
+ if (possibleAppMenuItems.length) {
+ let menuItem = document.createElement("menuseparator");
+ menuPopup.appendChild(menuItem);
+ menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("oncommand", "gApplicationsPane.manageApp(event)");
+ menuItem.setAttribute("label", this._prefsBundle.getString("manageApp"));
+ menuPopup.appendChild(menuItem);
+ }
+
+ // Select the item corresponding to the preferred action. If the always
+ // ask flag is set, it overrides the preferred action. Otherwise we pick
+ // the item identified by the preferred action (when the preferred action
+ // is to use a helper app, we have to pick the specific helper app item).
+ if (handlerInfo.alwaysAskBeforeHandling)
+ menu.selectedItem = askMenuItem;
+ else switch (handlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.handleInternally:
+ menu.selectedItem = internalMenuItem;
+ break;
+ case Ci.nsIHandlerInfo.useSystemDefault:
+ menu.selectedItem = defaultMenuItem;
+ break;
+ case Ci.nsIHandlerInfo.useHelperApp:
+ if (preferredApp)
+ menu.selectedItem =
+ possibleAppMenuItems.filter(function(v) v.handlerApp.equals(preferredApp))[0];
+ break;
+ case kActionUsePlugin:
+ menu.selectedItem = pluginMenuItem;
+ break;
+ case Ci.nsIHandlerInfo.saveToDisk:
+ menu.selectedItem = saveMenuItem;
+ break;
+ }
+ },
+
+
+ //**************************************************************************//
+ // Sorting & Filtering
+
+ _sortColumn: null,
+
+ /**
+ * Sort the list when the user clicks on a column header.
+ */
+ sort: function (event) {
+ var column = event.target;
+
+ // If the user clicked on a new sort column, remove the direction indicator
+ // from the old column.
+ if (this._sortColumn && this._sortColumn != column)
+ this._sortColumn.removeAttribute("sortDirection");
+
+ this._sortColumn = column;
+
+ // Set (or switch) the sort direction indicator.
+ if (column.getAttribute("sortDirection") == "ascending")
+ column.setAttribute("sortDirection", "descending");
+ else
+ column.setAttribute("sortDirection", "ascending");
+
+ this._sortVisibleTypes();
+ this._rebuildView();
+ },
+
+ /**
+ * Sort the list of visible types by the current sort column/direction.
+ */
+ _sortVisibleTypes: function() {
+ if (!this._sortColumn)
+ return;
+
+ var t = this;
+
+ function sortByType(a, b) {
+ return t._describeType(a).toLowerCase().
+ localeCompare(t._describeType(b).toLowerCase());
+ }
+
+ function sortByAction(a, b) {
+ return t._describePreferredAction(a).toLowerCase().
+ localeCompare(t._describePreferredAction(b).toLowerCase());
+ }
+
+ switch (this._sortColumn.getAttribute("value")) {
+ case "type":
+ this._visibleTypes.sort(sortByType);
+ break;
+ case "action":
+ this._visibleTypes.sort(sortByAction);
+ break;
+ }
+
+ if (this._sortColumn.getAttribute("sortDirection") == "descending")
+ this._visibleTypes.reverse();
+ },
+
+ /**
+ * Filter the list when the user enters a filter term into the filter field.
+ */
+ filter: function() {
+ this._rebuildView();
+ },
+
+ focusFilterBox: function() {
+ this._filter.focus();
+ this._filter.select();
+ },
+
+
+ //**************************************************************************//
+ // Changes
+
+ onSelectAction: function(aActionItem) {
+ this._storingAction = true;
+
+ try {
+ this._storeAction(aActionItem);
+ }
+ finally {
+ this._storingAction = false;
+ }
+ },
+
+ _storeAction: function(aActionItem) {
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+
+ let action = parseInt(aActionItem.getAttribute("action"));
+
+ // Set the plugin state if we're enabling or disabling a plugin.
+ if (action == kActionUsePlugin)
+ handlerInfo.enablePluginType();
+ else if (handlerInfo.pluginName && !handlerInfo.isDisabledPluginType)
+ handlerInfo.disablePluginType();
+
+ // Set the preferred application handler.
+ // We leave the existing preferred app in the list when we set
+ // the preferred action to something other than useHelperApp so that
+ // legacy datastores that don't have the preferred app in the list
+ // of possible apps still include the preferred app in the list of apps
+ // the user can choose to handle the type.
+ if (action == Ci.nsIHandlerInfo.useHelperApp)
+ handlerInfo.preferredApplicationHandler = aActionItem.handlerApp;
+
+ // Set the "always ask" flag.
+ if (action == Ci.nsIHandlerInfo.alwaysAsk)
+ handlerInfo.alwaysAskBeforeHandling = true;
+ else
+ handlerInfo.alwaysAskBeforeHandling = false;
+
+ // Set the preferred action.
+ handlerInfo.preferredAction = action;
+
+ handlerInfo.store();
+
+ // Make sure the handler info object is flagged to indicate that there is
+ // now some user configuration for the type.
+ handlerInfo.handledOnlyByPlugin = false;
+
+ // Update the action label and image to reflect the new preferred action.
+ typeItem.setAttribute("actionDescription",
+ this._describePreferredAction(handlerInfo));
+ if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
+ typeItem.setAttribute("actionIcon",
+ this._getIconURLForPreferredAction(handlerInfo));
+ }
+ },
+
+ manageApp: function(aEvent) {
+ // Don't let the normal "on select action" handler get this event,
+ // as we handle it specially ourselves.
+ aEvent.stopPropagation();
+
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/applicationManager.xul",
+ "", handlerInfo);
+
+ // Rebuild the actions menu so that we revert to the previous selection,
+ // or "Always ask" if the previous default application has been removed
+ this.rebuildActionsMenu();
+
+ // update the richlistitem too. Will be visible when selecting another row
+ typeItem.setAttribute("actionDescription",
+ this._describePreferredAction(handlerInfo));
+ if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
+ typeItem.setAttribute("actionIcon",
+ this._getIconURLForPreferredAction(handlerInfo));
+ }
+ },
+
+ chooseApp: function(aEvent) {
+ // Don't let the normal "on select action" handler get this event,
+ // as we handle it specially ourselves.
+ aEvent.stopPropagation();
+
+ var handlerApp;
+ let chooseAppCallback = function(aHandlerApp) {
+ // Rebuild the actions menu whether the user picked an app or canceled.
+ // If they picked an app, we want to add the app to the menu and select it.
+ // If they canceled, we want to go back to their previous selection.
+ this.rebuildActionsMenu();
+
+ // If the user picked a new app from the menu, select it.
+ if (aHandlerApp) {
+ let typeItem = this._list.selectedItem;
+ let actionsMenu =
+ document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+ let menuItems = actionsMenu.menupopup.childNodes;
+ for (let i = 0; i < menuItems.length; i++) {
+ let menuItem = menuItems[i];
+ if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
+ actionsMenu.selectedIndex = i;
+ this.onSelectAction(menuItem);
+ break;
+ }
+ }
+ }
+ }.bind(this);
+
+#ifdef XP_WIN
+ var params = {};
+ var handlerInfo = this._handledTypes[this._list.selectedItem.type];
+
+ if (isFeedType(handlerInfo.type)) {
+ // MIME info will be null, create a temp object.
+ params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type,
+ handlerInfo.primaryExtension);
+ } else {
+ params.mimeInfo = handlerInfo.wrappedHandlerInfo;
+ }
+
+ params.title = this._prefsBundle.getString("fpTitleChooseApp");
+ params.description = handlerInfo.description;
+ params.filename = null;
+ params.handlerApp = null;
+
+ window.openDialog("chrome://global/content/appPicker.xul", null,
+ "chrome,modal,centerscreen,titlebar,dialog=yes",
+ params);
+
+ if (this.isValidHandlerApp(params.handlerApp)) {
+ handlerApp = params.handlerApp;
+
+ // Add the app to the type's list of possible handlers.
+ handlerInfo.addPossibleApplicationHandler(handlerApp);
+ }
+
+ chooseAppCallback(handlerApp);
+#else
+ let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
+ this._isValidHandlerExecutable(fp.file)) {
+ handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsILocalHandlerApp);
+ handlerApp.name = getFileDisplayName(fp.file);
+ handlerApp.executable = fp.file;
+
+ // Add the app to the type's list of possible handlers.
+ let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+ handlerInfo.addPossibleApplicationHandler(handlerApp);
+
+ chooseAppCallback(handlerApp);
+ }
+ }.bind(this);
+
+ // Prompt the user to pick an app. If they pick one, and it's a valid
+ // selection, then add it to the list of possible handlers.
+ fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
+ fp.appendFilters(Ci.nsIFilePicker.filterApps);
+ fp.open(fpCallback);
+#endif
+ },
+
+ // Mark which item in the list was last selected so we can reselect it
+ // when we rebuild the list or when the user returns to the prefpane.
+ onSelectionChanged: function() {
+ if (this._list.selectedItem)
+ this._list.setAttribute("lastSelectedType",
+ this._list.selectedItem.getAttribute("type"));
+ },
+
+ _setIconClassForPreferredAction: function(aHandlerInfo, aElement) {
+ // If this returns true, the attribute that CSS sniffs for was set to something
+ // so you shouldn't manually set an icon URI.
+ // This removes the existing actionIcon attribute if any, even if returning false.
+ aElement.removeAttribute("actionIcon");
+
+ if (aHandlerInfo.alwaysAskBeforeHandling) {
+ aElement.setAttribute(APP_ICON_ATTR_NAME, "ask");
+ return true;
+ }
+
+ switch (aHandlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.saveToDisk:
+ aElement.setAttribute(APP_ICON_ATTR_NAME, "save");
+ return true;
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ if (isFeedType(aHandlerInfo.type)) {
+ aElement.setAttribute(APP_ICON_ATTR_NAME, "feed");
+ return true;
+ } else if (aHandlerInfo instanceof InternalHandlerInfoWrapper) {
+ aElement.setAttribute(APP_ICON_ATTR_NAME, "ask");
+ return true;
+ }
+ break;
+
+ case kActionUsePlugin:
+ aElement.setAttribute(APP_ICON_ATTR_NAME, "plugin");
+ return true;
+ }
+ aElement.removeAttribute(APP_ICON_ATTR_NAME);
+ return false;
+ },
+
+ _getIconURLForPreferredAction: function(aHandlerInfo) {
+ switch (aHandlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.useSystemDefault:
+ return this._getIconURLForSystemDefault(aHandlerInfo);
+
+ case Ci.nsIHandlerInfo.useHelperApp:
+ let preferredApp = aHandlerInfo.preferredApplicationHandler;
+ if (this.isValidHandlerApp(preferredApp))
+ return this._getIconURLForHandlerApp(preferredApp);
+ break;
+
+ // This should never happen, but if preferredAction is set to some weird
+ // value, then fall back to the generic application icon.
+ default:
+ return ICON_URL_APP;
+ }
+ },
+
+ _getIconURLForHandlerApp: function(aHandlerApp) {
+ if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
+ return this._getIconURLForFile(aHandlerApp.executable);
+
+ if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
+ return this._getIconURLForWebApp(aHandlerApp.uriTemplate);
+
+ if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
+ return this._getIconURLForWebApp(aHandlerApp.uri)
+
+ // We know nothing about other kinds of handler apps.
+ return "";
+ },
+
+ _getIconURLForFile: function(aFile) {
+ var fph = this._ioSvc.getProtocolHandler("file").
+ QueryInterface(Ci.nsIFileProtocolHandler);
+ var urlSpec = fph.getURLSpecFromFile(aFile);
+
+ return "moz-icon://" + urlSpec + "?size=16";
+ },
+
+ _getIconURLForWebApp: function(aWebAppURITemplate) {
+ var uri = this._ioSvc.newURI(aWebAppURITemplate, null, null);
+
+ // Unfortunately we can't use the favicon service to get the favicon,
+ // because the service looks for a record with the exact URL we give it, and
+ // users won't have such records for URLs they don't visit, and users won't
+ // visit the handler's URL template, they'll only visit URLs derived from
+ // that template (i.e. with %s in the template replaced by the URL of the
+ // content being handled).
+
+ if (/^https?$/.test(uri.scheme) && this._prefSvc.getBoolPref("browser.chrome.favicons"))
+ return uri.prePath + "/favicon.ico";
+
+ return "";
+ },
+
+ _getIconURLForSystemDefault: function(aHandlerInfo) {
+ // Handler info objects for MIME types on some OSes implement a property bag
+ // interface from which we can get an icon for the default app, so if we're
+ // dealing with a MIME type on one of those OSes, then try to get the icon.
+ if ("wrappedHandlerInfo" in aHandlerInfo) {
+ let wrappedHandlerInfo = aHandlerInfo.wrappedHandlerInfo;
+
+ if (wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
+ wrappedHandlerInfo instanceof Ci.nsIPropertyBag) {
+ try {
+ let url = wrappedHandlerInfo.getProperty("defaultApplicationIconURL");
+ if (url)
+ return url + "?size=16";
+ }
+ catch(ex) {}
+ }
+ }
+
+ // If this isn't a MIME type object on an OS that supports retrieving
+ // the icon, or if we couldn't retrieve the icon for some other reason,
+ // then use a generic icon.
+ return ICON_URL_APP;
+ }
+
+};
diff --git a/browser/components/preferences/applications.xul b/browser/components/preferences/applications.xul
new file mode 100644
index 000000000..2e6fa549e
--- /dev/null
+++ b/browser/components/preferences/applications.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: Java; 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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/applications.dtd">
+ %brandDTD;
+ %applicationsDTD;
+]>
+
+<overlay id="ApplicationsPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneApplications"
+ onpaneload="gApplicationsPane.init();"
+ flex="1"
+ helpTopic="prefs-applications">
+
+ <preferences id="feedsPreferences">
+ <preference id="browser.feeds.handler"
+ name="browser.feeds.handler"
+ type="string"/>
+ <preference id="browser.feeds.handler.default"
+ name="browser.feeds.handler.default"
+ type="string"/>
+ <preference id="browser.feeds.handlers.application"
+ name="browser.feeds.handlers.application"
+ type="file"/>
+ <preference id="browser.feeds.handlers.webservice"
+ name="browser.feeds.handlers.webservice"
+ type="string"/>
+
+ <preference id="browser.videoFeeds.handler"
+ name="browser.videoFeeds.handler"
+ type="string"/>
+ <preference id="browser.videoFeeds.handler.default"
+ name="browser.videoFeeds.handler.default"
+ type="string"/>
+ <preference id="browser.videoFeeds.handlers.application"
+ name="browser.videoFeeds.handlers.application"
+ type="file"/>
+ <preference id="browser.videoFeeds.handlers.webservice"
+ name="browser.videoFeeds.handlers.webservice"
+ type="string"/>
+
+ <preference id="browser.audioFeeds.handler"
+ name="browser.audioFeeds.handler"
+ type="string"/>
+ <preference id="browser.audioFeeds.handler.default"
+ name="browser.audioFeeds.handler.default"
+ type="string"/>
+ <preference id="browser.audioFeeds.handlers.application"
+ name="browser.audioFeeds.handlers.application"
+ type="file"/>
+ <preference id="browser.audioFeeds.handlers.webservice"
+ name="browser.audioFeeds.handlers.webservice"
+ type="string"/>
+
+ <preference id="pref.downloads.disable_button.edit_actions"
+ name="pref.downloads.disable_button.edit_actions"
+ type="bool"/>
+ </preferences>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/applications.js"/>
+
+ <keyset>
+ <key key="&focusSearch1.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
+ <key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
+ </keyset>
+
+ <hbox>
+ <textbox id="filter" flex="1"
+ type="search"
+ placeholder="&filter.emptytext;"
+ aria-controls="handlersView"
+ oncommand="gApplicationsPane.filter();"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <richlistbox id="handlersView" orient="vertical" persist="lastSelectedType"
+ preference="pref.downloads.disable_button.edit_actions"
+ onselect="gApplicationsPane.onSelectionChanged();">
+ <listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;">
+ <treecol id="typeColumn" label="&typeColumn.label;" value="type"
+ accesskey="&typeColumn.accesskey;" persist="sortDirection"
+ flex="1" onclick="gApplicationsPane.sort(event);"
+ sortDirection="ascending"/>
+ <treecol id="actionColumn" label="&actionColumn2.label;" value="action"
+ accesskey="&actionColumn2.accesskey;" persist="sortDirection"
+ flex="1" onclick="gApplicationsPane.sort(event);"/>
+ </listheader>
+ </richlistbox>
+ </prefpane>
+</overlay>
diff --git a/browser/components/preferences/colors.xul b/browser/components/preferences/colors.xul
new file mode 100644
index 000000000..caf8c8c0e
--- /dev/null
+++ b/browser/components/preferences/colors.xul
@@ -0,0 +1,114 @@
+<?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 prefwindow SYSTEM "chrome://browser/locale/preferences/colors.dtd" >
+
+<prefwindow id="ColorsDialog" type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&colorsDialog.title;"
+ dlgbuttons="accept,cancel,help"
+ ondialoghelp="openPrefsHelp()"
+ style="width: &window.width; !important;">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+ <prefpane id="ColorsDialogPane"
+ helpTopic="prefs-fonts-and-colors">
+
+ <preferences>
+ <preference id="browser.display.document_color_use" name="browser.display.document_color_use" type="int"/>
+ <preference id="browser.anchor_color" name="browser.anchor_color" type="string"/>
+ <preference id="browser.visited_color" name="browser.visited_color" type="string"/>
+ <preference id="browser.underline_anchors" name="browser.underline_anchors" type="bool"/>
+ <preference id="browser.display.foreground_color" name="browser.display.foreground_color" type="string"/>
+ <preference id="browser.display.background_color" name="browser.display.background_color" type="string"/>
+ <preference id="browser.display.use_system_colors" name="browser.display.use_system_colors" type="bool"/>
+ <preference id="browser.display.prefers_color_scheme" name="browser.display.prefers_color_scheme" type="int"/>
+ </preferences>
+
+ <hbox>
+ <groupbox flex="1">
+ <caption label="&color;"/>
+ <hbox align="center">
+ <label value="&textColor.label;" accesskey="&textColor.accesskey;" control="foregroundtextmenu"/>
+ <spacer flex="1"/>
+ <colorpicker type="button" id="foregroundtextmenu" palettename="standard"
+ preference="browser.display.foreground_color"/>
+ </hbox>
+ <hbox align="center" style="margin-top: 5px">
+ <label value="&backgroundColor.label;" accesskey="&backgroundColor.accesskey;" control="backgroundmenu"/>
+ <spacer flex="1"/>
+ <colorpicker type="button" id="backgroundmenu" palettename="standard"
+ preference="browser.display.background_color"/>
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="center">
+ <checkbox id="browserUseSystemColors" label="&useSystemColors.label;" accesskey="&useSystemColors.accesskey;"
+ preference="browser.display.use_system_colors"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox flex="1">
+ <caption label="&links;"/>
+ <hbox align="center">
+ <label value="&linkColor.label;" accesskey="&linkColor.accesskey;" control="unvisitedlinkmenu"/>
+ <spacer flex="1"/>
+ <colorpicker type="button" id="unvisitedlinkmenu" palettename="standard"
+ preference="browser.anchor_color"/>
+ </hbox>
+ <hbox align="center" style="margin-top: 5px">
+ <label value="&visitedLinkColor.label;" accesskey="&visitedLinkColor.accesskey;" control="visitedlinkmenu"/>
+ <spacer flex="1"/>
+ <colorpicker type="button" id="visitedlinkmenu" palettename="standard"
+ preference="browser.visited_color"/>
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="center">
+ <checkbox id="browserUnderlineAnchors" label="&underlineLinks.label;" accesskey="&underlineLinks.accesskey;"
+ preference="browser.underline_anchors"/>
+ </hbox>
+ </groupbox>
+ </hbox>
+#ifdef XP_WIN
+ <vbox align="start">
+#else
+ <vbox>
+#endif
+ <label accesskey="&overridePageColors.accesskey;"
+ control="useDocumentColors">&overridePageColors.label;</label>
+ <menulist id="useDocumentColors" preference="browser.display.document_color_use">
+ <menupopup>
+ <menuitem label="&overridePageColors.always.label;"
+ value="2" id="documentColorAlways"/>
+ <menuitem label="&overridePageColors.auto.label;"
+ value="0" id="documentColorAutomatic"/>
+ <menuitem label="&overridePageColors.never.label;"
+ value="1" id="documentColorNever"/>
+ </menupopup>
+ </menulist>
+ </vbox>
+
+ <groupbox>
+ <caption label="&prefersColorScheme.caption;"/>
+ <label control="prefersColorSchemeSelection">&prefersColorScheme.label;</label>
+ <radiogroup id="prefersColorSchemeSelection"
+ preference="browser.display.prefers_color_scheme">
+ <radio value="1"
+ label="&prefersColorSchemeLight.label;"
+ accesskey="&prefersColorSchemeLight.accesskey;"/>
+ <radio value="2"
+ label="&prefersColorSchemeDark.label;"
+ accesskey="&prefersColorSchemeDark.accesskey;"/>
+ <radio value="0"
+ label="&prefersColorSchemeDisabled.label;"
+ accesskey="&prefersColorSchemeDisabled.accesskey;"/>
+ </radiogroup>
+ <description>&prefersColorSchemeWarning;</description>
+ </groupbox>
+
+ </prefpane>
+</prefwindow>
diff --git a/browser/components/preferences/connection.js b/browser/components/preferences/connection.js
new file mode 100644
index 000000000..f94819d3f
--- /dev/null
+++ b/browser/components/preferences/connection.js
@@ -0,0 +1,199 @@
+// 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 gConnectionsDialog = {
+ beforeAccept: function ()
+ {
+ var proxyTypePref = document.getElementById("network.proxy.type");
+ if (proxyTypePref.value == 2) {
+ this.doAutoconfigURLFixup();
+ return true;
+ }
+
+ if (proxyTypePref.value != 1)
+ return true;
+
+ var httpProxyURLPref = document.getElementById("network.proxy.http");
+ var httpProxyPortPref = document.getElementById("network.proxy.http_port");
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ if (shareProxiesPref.value) {
+ var proxyPrefs = ["ssl", "ftp", "socks"];
+ for (var i = 0; i < proxyPrefs.length; ++i) {
+ var proxyServerURLPref = document.getElementById("network.proxy." + proxyPrefs[i]);
+ var proxyPortPref = document.getElementById("network.proxy." + proxyPrefs[i] + "_port");
+ var backupServerURLPref = document.getElementById("network.proxy.backup." + proxyPrefs[i]);
+ var backupPortPref = document.getElementById("network.proxy.backup." + proxyPrefs[i] + "_port");
+ backupServerURLPref.value = proxyServerURLPref.value;
+ backupPortPref.value = proxyPortPref.value;
+ proxyServerURLPref.value = httpProxyURLPref.value;
+ proxyPortPref.value = httpProxyPortPref.value;
+ }
+ }
+
+ this.sanitizeNoProxiesPref();
+
+ return true;
+ },
+
+ checkForSystemProxy: function ()
+ {
+ if ("@mozilla.org/system-proxy-settings;1" in Components.classes)
+ document.getElementById("systemPref").removeAttribute("hidden");
+ },
+
+ proxyTypeChanged: function ()
+ {
+ var proxyTypePref = document.getElementById("network.proxy.type");
+
+ // Update http
+ var httpProxyURLPref = document.getElementById("network.proxy.http");
+ httpProxyURLPref.disabled = proxyTypePref.value != 1;
+ var httpProxyPortPref = document.getElementById("network.proxy.http_port");
+ httpProxyPortPref.disabled = proxyTypePref.value != 1;
+
+ // Now update the other protocols
+ this.updateProtocolPrefs();
+
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ shareProxiesPref.disabled = proxyTypePref.value != 1;
+
+ var autologinProxyPref = document.getElementById("signon.autologin.proxy");
+ autologinProxyPref.disabled = proxyTypePref.value == 0;
+
+ var noProxiesPref = document.getElementById("network.proxy.no_proxies_on");
+ noProxiesPref.disabled = proxyTypePref.value == 0;
+
+ var autoconfigURLPref = document.getElementById("network.proxy.autoconfig_url");
+ autoconfigURLPref.disabled = proxyTypePref.value != 2;
+
+ this.updateReloadButton();
+ },
+
+ updateDNSPref: function ()
+ {
+ var socksVersionPref = document.getElementById("network.proxy.socks_version");
+ var socksDNSPref = document.getElementById("network.proxy.socks_remote_dns");
+ var proxyTypePref = document.getElementById("network.proxy.type");
+ var isDefinitelySocks4 = !socksVersionPref.disabled && socksVersionPref.value == 4;
+ socksDNSPref.disabled = (isDefinitelySocks4 || proxyTypePref.value == 0);
+ return undefined;
+ },
+
+ updateReloadButton: function ()
+ {
+ // Disable the "Reload PAC" button if the selected proxy type is not PAC or
+ // if the current value of the PAC textbox does not match the value stored
+ // in prefs. Likewise, disable the reload button if PAC is not configured
+ // in prefs.
+
+ var typedURL = document.getElementById("networkProxyAutoconfigURL").value;
+ var proxyTypeCur = document.getElementById("network.proxy.type").value;
+
+ var prefs =
+ Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ var pacURL = prefs.getCharPref("network.proxy.autoconfig_url");
+ var proxyType = prefs.getIntPref("network.proxy.type");
+
+ var disableReloadPref =
+ document.getElementById("pref.advanced.proxies.disable_button.reload");
+ disableReloadPref.disabled =
+ (proxyTypeCur != 2 || proxyType != 2 || typedURL != pacURL);
+ },
+
+ readProxyType: function ()
+ {
+ this.proxyTypeChanged();
+ return undefined;
+ },
+
+ updateProtocolPrefs: function ()
+ {
+ var proxyTypePref = document.getElementById("network.proxy.type");
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ var proxyPrefs = ["ssl", "ftp", "socks"];
+ for (var i = 0; i < proxyPrefs.length; ++i) {
+ var proxyServerURLPref = document.getElementById("network.proxy." + proxyPrefs[i]);
+ var proxyPortPref = document.getElementById("network.proxy." + proxyPrefs[i] + "_port");
+
+ // Restore previous per-proxy custom settings, if present.
+ if (!shareProxiesPref.value) {
+ var backupServerURLPref = document.getElementById("network.proxy.backup." + proxyPrefs[i]);
+ var backupPortPref = document.getElementById("network.proxy.backup." + proxyPrefs[i] + "_port");
+ if (backupServerURLPref.hasUserValue) {
+ proxyServerURLPref.value = backupServerURLPref.value;
+ backupServerURLPref.reset();
+ }
+ if (backupPortPref.hasUserValue) {
+ proxyPortPref.value = backupPortPref.value;
+ backupPortPref.reset();
+ }
+ }
+
+ proxyServerURLPref.updateElements();
+ proxyPortPref.updateElements();
+ proxyServerURLPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value;
+ proxyPortPref.disabled = proxyServerURLPref.disabled;
+ }
+ var socksVersionPref = document.getElementById("network.proxy.socks_version");
+ socksVersionPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value;
+ this.updateDNSPref();
+ return undefined;
+ },
+
+ readProxyProtocolPref: function (aProtocol, aIsPort)
+ {
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ if (shareProxiesPref.value) {
+ var pref = document.getElementById("network.proxy.http" + (aIsPort ? "_port" : ""));
+ return pref.value;
+ }
+
+ var backupPref = document.getElementById("network.proxy.backup." + aProtocol + (aIsPort ? "_port" : ""));
+ return backupPref.hasUserValue ? backupPref.value : undefined;
+ },
+
+ reloadPAC: function ()
+ {
+ Components.classes["@mozilla.org/network/protocol-proxy-service;1"].
+ getService().reloadPAC();
+ },
+
+ doAutoconfigURLFixup: function ()
+ {
+ var autoURL = document.getElementById("networkProxyAutoconfigURL");
+ var autoURLPref = document.getElementById("network.proxy.autoconfig_url");
+ var URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
+ .getService(Components.interfaces.nsIURIFixup);
+ try {
+ autoURLPref.value = autoURL.value = URIFixup.createFixupURI(autoURL.value, 0).spec;
+ } catch(ex) {}
+ },
+
+ sanitizeNoProxiesPref: function()
+ {
+ var noProxiesPref = document.getElementById("network.proxy.no_proxies_on");
+ // replace substrings of ; and \n with commas if they're neither immediately
+ // preceded nor followed by a valid separator character
+ noProxiesPref.value = noProxiesPref.value.replace(/([^, \n;])[;\n]+(?![,\n;])/g, '$1,');
+ // replace any remaining ; and \n since some may follow commas, etc.
+ noProxiesPref.value = noProxiesPref.value.replace(/[;\n]/g, '');
+ },
+
+ readHTTPProxyServer: function ()
+ {
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ if (shareProxiesPref.value)
+ this.updateProtocolPrefs();
+ return undefined;
+ },
+
+ readHTTPProxyPort: function ()
+ {
+ var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
+ if (shareProxiesPref.value)
+ this.updateProtocolPrefs();
+ return undefined;
+ }
+};
diff --git a/browser/components/preferences/connection.xul b/browser/components/preferences/connection.xul
new file mode 100644
index 000000000..e21168652
--- /dev/null
+++ b/browser/components/preferences/connection.xul
@@ -0,0 +1,159 @@
+<?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/. -->
+
+<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/connection.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<prefwindow id="ConnectionsDialog" type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&connectionsDialog.title;"
+ dlgbuttons="accept,cancel,help"
+ onbeforeaccept="return gConnectionsDialog.beforeAccept();"
+ onload="gConnectionsDialog.checkForSystemProxy();"
+ ondialoghelp="openPrefsHelp()"
+ style="width: &window.width; !important;">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+
+ <prefpane id="ConnectionsDialogPane"
+ helpTopic="prefs-connection-settings">
+
+ <preferences>
+ <preference id="network.proxy.type" name="network.proxy.type" type="int"
+ onchange="gConnectionsDialog.proxyTypeChanged();"/>
+ <preference id="network.proxy.http" name="network.proxy.http" type="string"/>
+ <preference id="network.proxy.http_port" name="network.proxy.http_port" type="int"/>
+ <preference id="network.proxy.ftp" name="network.proxy.ftp" type="string"/>
+ <preference id="network.proxy.ftp_port" name="network.proxy.ftp_port" type="int"/>
+ <preference id="network.proxy.ssl" name="network.proxy.ssl" type="string"/>
+ <preference id="network.proxy.ssl_port" name="network.proxy.ssl_port" type="int"/>
+ <preference id="network.proxy.socks" name="network.proxy.socks" type="string"/>
+ <preference id="network.proxy.socks_port" name="network.proxy.socks_port" type="int"/>
+ <preference id="network.proxy.socks_version" name="network.proxy.socks_version" type="int"
+ onchange="gConnectionsDialog.updateDNSPref();"/>
+ <preference id="network.proxy.socks_remote_dns" name="network.proxy.socks_remote_dns" type="bool"/>
+ <preference id="network.proxy.no_proxies_on" name="network.proxy.no_proxies_on" type="string"/>
+ <preference id="network.proxy.autoconfig_url" name="network.proxy.autoconfig_url" type="string"/>
+ <preference id="network.proxy.share_proxy_settings" name="network.proxy.share_proxy_settings" type="bool"/>
+ <preference id="signon.autologin.proxy" name="signon.autologin.proxy" type="bool"/>
+ <preference id="pref.advanced.proxies.disable_button.reload"
+ name="pref.advanced.proxies.disable_button.reload" type="bool"/>
+ <preference id="network.proxy.backup.ftp" name="network.proxy.backup.ftp" type="string"/>
+ <preference id="network.proxy.backup.ftp_port" name="network.proxy.backup.ftp_port" type="int"/>
+ <preference id="network.proxy.backup.ssl" name="network.proxy.backup.ssl" type="string"/>
+ <preference id="network.proxy.backup.ssl_port" name="network.proxy.backup.ssl_port" type="int"/>
+ <preference id="network.proxy.backup.socks" name="network.proxy.backup.socks" type="string"/>
+ <preference id="network.proxy.backup.socks_port" name="network.proxy.backup.socks_port" type="int"/>
+ </preferences>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/connection.js"/>
+
+ <stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <groupbox>
+ <caption label="&proxyTitle.label;"/>
+
+ <radiogroup id="networkProxyType" preference="network.proxy.type"
+ onsyncfrompreference="return gConnectionsDialog.readProxyType();">
+ <radio value="0" label="&noProxyTypeRadio.label;" accesskey="&noProxyTypeRadio.accesskey;"/>
+ <radio value="4" label="&WPADTypeRadio.label;" accesskey="&WPADTypeRadio.accesskey;"/>
+ <radio value="5" label="&systemTypeRadio.label;" accesskey="&systemTypeRadio.accesskey;" id="systemPref" hidden="true"/>
+ <radio value="1" label="&manualTypeRadio.label;" accesskey="&manualTypeRadio.accesskey;"/>
+ <grid class="indent" flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <hbox pack="end">
+ <label value="&http.label;" accesskey="&http.accesskey;" control="networkProxyHTTP"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxyHTTP" flex="1"
+ preference="network.proxy.http" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyServer();"/>
+ <label value="&port.label;" accesskey="&HTTPport.accesskey;" control="networkProxyHTTP_Port"/>
+ <textbox id="networkProxyHTTP_Port" type="number" max="65535" size="5"
+ preference="network.proxy.http_port" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyPort();"/>
+ </hbox>
+ </row>
+ <row>
+ <hbox/>
+ <hbox>
+ <checkbox id="shareAllProxies" label="&shareproxy.label;" accesskey="&shareproxy.accesskey;"
+ preference="network.proxy.share_proxy_settings"
+ onsyncfrompreference="return gConnectionsDialog.updateProtocolPrefs();"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <hbox pack="end">
+ <label value="&ssl.label;" accesskey="&ssl.accesskey;" control="networkProxySSL"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxySSL" flex="1" preference="network.proxy.ssl"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', false);"/>
+ <label value="&port.label;" accesskey="&SSLport.accesskey;" control="networkProxySSL_Port"/>
+ <textbox id="networkProxySSL_Port" type="number" max="65535" size="5" preference="network.proxy.ssl_port"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', true);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <hbox pack="end">
+ <label value="&ftp.label;" accesskey="&ftp.accesskey;" control="networkProxyFTP"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxyFTP" flex="1" preference="network.proxy.ftp"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', false);"/>
+ <label value="&port.label;" accesskey="&FTPport.accesskey;" control="networkProxyFTP_Port"/>
+ <textbox id="networkProxyFTP_Port" type="number" max="65535" size="5" preference="network.proxy.ftp_port"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', true);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <hbox pack="end">
+ <label value="&socks.label;" accesskey="&socks.accesskey;" control="networkProxySOCKS"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxySOCKS" flex="1" preference="network.proxy.socks"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', false);"/>
+ <label value="&port.label;" accesskey="&SOCKSport.accesskey;" control="networkProxySOCKS_Port"/>
+ <textbox id="networkProxySOCKS_Port" type="number" max="65535" size="5" preference="network.proxy.socks_port"
+ onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', true);"/>
+ </hbox>
+ </row>
+ <row>
+ <spacer/>
+ <radiogroup id="networkProxySOCKSVersion" orient="horizontal"
+ preference="network.proxy.socks_version">
+ <radio id="networkProxySOCKSVersion4" value="4" label="&socks4.label;" accesskey="&socks4.accesskey;"/>
+ <radio id="networkProxySOCKSVersion5" value="5" label="&socks5.label;" accesskey="&socks5.accesskey;"/>
+ </radiogroup>
+ </row>
+ </rows>
+ </grid>
+ <radio value="2" label="&autoTypeRadio.label;" accesskey="&autoTypeRadio.accesskey;"/>
+ <hbox class="indent" flex="1" align="center">
+ <textbox id="networkProxyAutoconfigURL" flex="1" preference="network.proxy.autoconfig_url"
+ oninput="gConnectionsDialog.updateReloadButton();"/>
+ <button id="autoReload" icon="refresh"
+ label="&reload.label;" accesskey="&reload.accesskey;"
+ oncommand="gConnectionsDialog.reloadPAC();"
+ preference="pref.advanced.proxies.disable_button.reload"/>
+ </hbox>
+ </radiogroup>
+ <separator class="thin"/>
+ <label value="&noproxy.label;" accesskey="&noproxy.accesskey;" control="networkProxyNone"/>
+ <textbox id="networkProxyNone" preference="network.proxy.no_proxies_on" multiline="true" rows="2"/>
+ <label value="&noproxyExplain.label;" control="networkProxyNone"/>
+ <checkbox id="autologinProxy" preference="signon.autologin.proxy"
+ label="&autologinproxy.label;" accesskey="&autologinproxy.accesskey;"
+ tooltiptext="&autologinproxy.tooltip;"/>
+ <checkbox id="networkProxySOCKSRemoteDNS" preference="network.proxy.socks_remote_dns"
+ label="&socksRemoteDNS.label;" accesskey="&socksRemoteDNS.accesskey;"/>
+ </groupbox>
+ </prefpane>
+</prefwindow>
diff --git a/browser/components/preferences/content.js b/browser/components/preferences/content.js
new file mode 100644
index 000000000..62a675c92
--- /dev/null
+++ b/browser/components/preferences/content.js
@@ -0,0 +1,186 @@
+// 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 gContentPane = {
+
+ /**
+ * Initializes the fonts dropdowns displayed in this pane.
+ */
+ init: function ()
+ {
+ this._rebuildFonts();
+ var menulist = document.getElementById("defaultFont");
+ if (menulist.selectedIndex == -1) {
+ menulist.insertItemAt(0, "", "", "");
+ menulist.selectedIndex = 0;
+ }
+ },
+
+ // UTILITY FUNCTIONS
+
+ /**
+ * Utility function to enable/disable the button specified by aButtonID based
+ * on the value of the Boolean preference specified by aPreferenceID.
+ */
+ updateButtons: function (aButtonID, aPreferenceID)
+ {
+ var button = document.getElementById(aButtonID);
+ var preference = document.getElementById(aPreferenceID);
+ button.disabled = preference.value != true;
+ return undefined;
+ },
+
+ /**
+ * Utility function to enable/disable the checkboxes for MSE options depending
+ * on the value of media.mediasource.enabled.
+ */
+ updateMSE: function ()
+ {
+ var checkboxMSEMP4 = document.getElementById('videoMSEMP4');
+ var checkboxMSEWebM = document.getElementById('videoMSEWebM');
+ var preference = document.getElementById('media.mediasource.enabled');
+ checkboxMSEMP4.disabled = preference.value != true;
+ checkboxMSEWebM.disabled = preference.value != true;
+ },
+
+ // BEGIN UI CODE
+
+ /*
+ * Preferences:
+ *
+ * dom.disable_open_during_load
+ * - true if popups are blocked by default, false otherwise
+ */
+
+ // POP-UPS
+
+ /**
+ * Displays the popup exceptions dialog where specific site popup preferences
+ * can be set.
+ */
+ showPopupExceptions: function ()
+ {
+ var bundlePreferences = document.getElementById("bundlePreferences");
+ var params = { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup" };
+ params.windowTitle = bundlePreferences.getString("popuppermissionstitle");
+ params.introText = bundlePreferences.getString("popuppermissionstext");
+ document.documentElement.openWindow("Browser:Permissions",
+ "chrome://browser/content/preferences/permissions.xul",
+ "", params);
+ },
+
+
+ // FONTS
+
+ /**
+ * Populates the default font list in UI.
+ */
+ _rebuildFonts: function ()
+ {
+ var langGroupPref = document.getElementById("font.language.group");
+ this._selectDefaultLanguageGroup(langGroupPref.value,
+ this._readDefaultFontTypeForLanguage(langGroupPref.value) == "serif");
+ },
+
+ /**
+ *
+ */
+ _selectDefaultLanguageGroup: function (aLanguageGroup, aIsSerif)
+ {
+ const kFontNameFmtSerif = "font.name.serif.%LANG%";
+ const kFontNameFmtSansSerif = "font.name.sans-serif.%LANG%";
+ const kFontNameListFmtSerif = "font.name-list.serif.%LANG%";
+ const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
+ const kFontSizeFmtVariable = "font.size.variable.%LANG%";
+
+ var prefs = [{ format : aIsSerif ? kFontNameFmtSerif : kFontNameFmtSansSerif,
+ type : "fontname",
+ element : "defaultFont",
+ fonttype : aIsSerif ? "serif" : "sans-serif" },
+ { format : aIsSerif ? kFontNameListFmtSerif : kFontNameListFmtSansSerif,
+ type : "unichar",
+ element : null,
+ fonttype : aIsSerif ? "serif" : "sans-serif" },
+ { format : kFontSizeFmtVariable,
+ type : "int",
+ element : "defaultFontSize",
+ fonttype : null }];
+ var preferences = document.getElementById("contentPreferences");
+ for (var i = 0; i < prefs.length; ++i) {
+ var preference = document.getElementById(prefs[i].format.replace(/%LANG%/, aLanguageGroup));
+ if (!preference) {
+ preference = document.createElement("preference");
+ var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup);
+ preference.id = name;
+ preference.setAttribute("name", name);
+ preference.setAttribute("type", prefs[i].type);
+ preferences.appendChild(preference);
+ }
+
+ if (!prefs[i].element)
+ continue;
+
+ var element = document.getElementById(prefs[i].element);
+ if (element) {
+ element.setAttribute("preference", preference.id);
+
+ if (prefs[i].fonttype)
+ FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element);
+
+ preference.setElementValue(element);
+ }
+ }
+ },
+
+ /**
+ * Returns the type of the current default font for the language denoted by
+ * aLanguageGroup.
+ */
+ _readDefaultFontTypeForLanguage: function (aLanguageGroup)
+ {
+ const kDefaultFontType = "font.default.%LANG%";
+ var defaultFontTypePref = kDefaultFontType.replace(/%LANG%/, aLanguageGroup);
+ var preference = document.getElementById(defaultFontTypePref);
+ if (!preference) {
+ preference = document.createElement("preference");
+ preference.id = defaultFontTypePref;
+ preference.setAttribute("name", defaultFontTypePref);
+ preference.setAttribute("type", "string");
+ preference.setAttribute("onchange", "gContentPane._rebuildFonts();");
+ document.getElementById("contentPreferences").appendChild(preference);
+ }
+ return preference.value;
+ },
+
+ /**
+ * Displays the fonts dialog, where web page font names and sizes can be
+ * configured.
+ */
+ configureFonts: function ()
+ {
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/fonts.xul",
+ "", null);
+ },
+
+ /**
+ * Displays the colors dialog, where default web page/link/etc. colors can be
+ * configured.
+ */
+ configureColors: function ()
+ {
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/colors.xul",
+ "", null);
+ },
+
+ // LANGUAGES
+
+ /**
+ * Shows a dialog in which the preferred language for web content may be set.
+ */
+ showLanguages: function ()
+ {
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/languages.xul",
+ "", null);
+ }
+};
diff --git a/browser/components/preferences/content.xul b/browser/components/preferences/content.xul
new file mode 100644
index 000000000..21f9e5d81
--- /dev/null
+++ b/browser/components/preferences/content.xul
@@ -0,0 +1,209 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: Java; tab-width: 4; 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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % contentDTD SYSTEM "chrome://browser/locale/preferences/content.dtd">
+ %brandDTD;
+ %contentDTD;
+]>
+
+<overlay id="ContentPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneContent"
+ onpaneload="gContentPane.init();"
+ helpTopic="prefs-content">
+
+ <preferences id="contentPreferences">
+ <!--XXX buttons prefs -->
+
+ <!-- POPUPS, IMAGES -->
+ <preference id="dom.disable_open_during_load" name="dom.disable_open_during_load" type="bool"/>
+ <preference id="permissions.default.image" name="permissions.default.image" type="int"/>
+
+ <!-- FONTS -->
+ <preference id="font.language.group"
+ name="font.language.group"
+ type="wstring"
+ onchange="gContentPane._rebuildFonts();"/>
+
+ <!-- JavaScript -->
+ <preference id="javascript.options.wasm" name="javascript.options.wasm" type="bool"/>
+
+
+ <!-- VIDEO -->
+ <preference id="media.mediasource.enabled" name="media.mediasource.enabled" type="bool"/>
+ <preference id="media.mediasource.mp4.enabled" name="media.mediasource.mp4.enabled" type="bool"/>
+ <preference id="media.mediasource.webm.enabled" name="media.mediasource.webm.enabled" type="bool"/>
+
+ <!-- Media formats -->
+ <preference id="media.av1.enabled" name="media.av1.enabled" type="bool"/>
+ <preference id="media.flac.enabled" name="media.flac.enabled" type="bool"/>
+ <preference id="media.mp4.enabled" name="media.mp4.enabled" type="bool"/>
+ <preference id="media.ogg.enabled" name="media.ogg.enabled" type="bool"/>
+ <preference id="media.opus.enabled" name="media.opus.enabled" type="bool"/>
+ <preference id="media.webm.enabled" name="media.webm.enabled" type="bool"/>
+
+ </preferences>
+
+ <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/>
+ <script type="application/javascript" src="chrome://browser/content/preferences/content.js"/>
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <!-- various checkboxes, font-fu -->
+ <groupbox id="miscGroup">
+ <grid id="contentGrid">
+ <columns>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows id="contentRows-1">
+ <row id="popupPolicyRow">
+ <vbox align="start">
+ <checkbox id="popupPolicy" preference="dom.disable_open_during_load"
+ label="&blockPopups.label;" accesskey="&blockPopups.accesskey;"
+ onsyncfrompreference="return gContentPane.updateButtons('popupPolicyButton',
+ 'dom.disable_open_during_load');"/>
+ </vbox>
+ <button id="popupPolicyButton" label="&popupExceptions.label;"
+ oncommand="gContentPane.showPopupExceptions();"
+ accesskey="&popupExceptions.accesskey;"/>
+ </row>
+ <row id="enableImagesRow">
+ <hbox align="center">
+ <label id="loadImages" control="loadImages-menu">&loadImages.label;</label>
+ <menulist id="loadImages-menu" preference="permissions.default.image" sizetopopup="always">
+ <menupopup>
+ <menuitem label="&loadImages.always;" value="1" />
+ <menuitem label="&loadImages.never;" value="2" />
+ <menuitem label="&loadImages.no3rdparty;" value="3" />
+ </menupopup>
+ </menulist>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- Fonts and Colors -->
+ <groupbox id="fontsGroup">
+ <caption label="&fontsAndColors.label;"/>
+
+ <grid id="fontsGrid">
+ <columns>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows id="fontsRows">
+ <row id="fontRow">
+ <hbox align="center">
+ <label control="defaultFont" accesskey="&defaultFont.accesskey;">&defaultFont.label;</label>
+ <menulist id="defaultFont" flex="1"/>
+ <label control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label>
+ <menulist id="defaultFontSize">
+ <menupopup>
+ <menuitem value="9" label="9"/>
+ <menuitem value="10" label="10"/>
+ <menuitem value="11" label="11"/>
+ <menuitem value="12" label="12"/>
+ <menuitem value="13" label="13"/>
+ <menuitem value="14" label="14"/>
+ <menuitem value="15" label="15"/>
+ <menuitem value="16" label="16"/>
+ <menuitem value="17" label="17"/>
+ <menuitem value="18" label="18"/>
+ <menuitem value="20" label="20"/>
+ <menuitem value="22" label="22"/>
+ <menuitem value="24" label="24"/>
+ <menuitem value="26" label="26"/>
+ <menuitem value="28" label="28"/>
+ <menuitem value="30" label="30"/>
+ <menuitem value="32" label="32"/>
+ <menuitem value="34" label="34"/>
+ <menuitem value="36" label="36"/>
+ <menuitem value="40" label="40"/>
+ <menuitem value="44" label="44"/>
+ <menuitem value="48" label="48"/>
+ <menuitem value="56" label="56"/>
+ <menuitem value="64" label="64"/>
+ <menuitem value="72" label="72"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <button id="advancedFonts" icon="select-font"
+ label="&advancedFonts.label;"
+ accesskey="&advancedFonts.accesskey;"
+ oncommand="gContentPane.configureFonts();"/>
+ </row>
+ <row id="colorsRow">
+ <hbox/>
+ <button id="colors" icon="select-color"
+ label="&colors.label;"
+ accesskey="&colors.accesskey;"
+ oncommand="gContentPane.configureColors();"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- Languages -->
+ <groupbox id="languagesGroup">
+ <caption label="&languages.label;"/>
+
+ <hbox id="languagesBox" align="center">
+ <description flex="1" control="chooseLanguage">&chooseLanguage.label;</description>
+ <button id="chooseLanguage"
+ label="&chooseButton.label;"
+ accesskey="&chooseButton.accesskey;"
+ oncommand="gContentPane.showLanguages();"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Javascript -->
+ <groupbox id="jsOptionsGroup">
+ <caption label="&jsOptions.label;"/>
+
+ <checkbox id="jsOptionsWasm" preference="javascript.options.wasm"
+ label="&jsOptionsWasm.label;" accesskey="&jsOptionsWasm.accesskey;"/>
+ </groupbox>
+
+ <!-- Video -->
+ <groupbox id="videoGroup">
+ <caption label="&video.label;"/>
+
+ <checkbox id="videoMSE" preference="media.mediasource.enabled"
+ label="&videoMSE.label;" accesskey="&videoMSE.accesskey;"
+ onsyncfrompreference="gContentPane.updateMSE();"/>
+ <checkbox class="indent" id="videoMSEMP4" preference="media.mediasource.mp4.enabled"
+ label="&videoMSEMP4.label;" accesskey="&videoMSEMP4.accesskey;"/>
+ <checkbox class="indent" id="videoMSEWebM" preference="media.mediasource.webm.enabled"
+ label="&videoMSEWebM.label;" accesskey="&videoMSEWebM.accesskey;"/>
+ </groupbox>
+
+ <!-- Media formats -->
+ <groupbox id="mediaSupport" align="start">
+ <caption label="&mediaSupport.label;"/>
+ <hbox align="center">
+ <label id="allowEnable" value="&allowEnable.label;"/>
+#ifdef MOZ_FMP4
+ <checkbox id="enableMP4" label="&enableMP4.label;" preference="media.mp4.enabled"/>
+#endif
+ <checkbox id="enableWebM" label="&enableWebM.label;" preference="media.webm.enabled"/>
+#ifdef MOZ_AV1
+ <checkbox id="enableAV1" label="&enableAV1.label;" preference="media.av1.enabled"/>
+#endif
+ <checkbox id="enableOGG" label="&enableOGG.label;" preference="media.ogg.enabled"/>
+ <checkbox id="enableOPUS" label="&enableOPUS.label;" preference="media.opus.enabled"/>
+ <checkbox id="enableFLAC" label="&enableFLAC.label;" preference="media.flac.enabled"/>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+
+</overlay>
diff --git a/browser/components/preferences/cookies.js b/browser/components/preferences/cookies.js
new file mode 100644
index 000000000..dbc2b3ef6
--- /dev/null
+++ b/browser/components/preferences/cookies.js
@@ -0,0 +1,943 @@
+// 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 nsICookie = Components.interfaces.nsICookie;
+
+Components.utils.import("resource://gre/modules/PluralForm.jsm");
+
+var gCookiesWindow = {
+ _cm : Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager),
+ _ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
+ .getService(Components.interfaces.nsIScriptableDateFormat),
+ _hosts : {},
+ _hostOrder : [],
+ _tree : null,
+ _bundle : null,
+
+ init: function() {
+ var os = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ os.addObserver(this, "cookie-changed", false);
+ os.addObserver(this, "perm-changed", false);
+
+ this._bundle = document.getElementById("bundlePreferences");
+ this._tree = document.getElementById("cookiesList");
+
+ let removeAllCookies = document.getElementById("removeAllCookies");
+ removeAllCookies.setAttribute("accesskey", this._bundle.getString("removeAllCookies.accesskey"));
+ let removeSelectedCookies = document.getElementById("removeSelectedCookies");
+ removeSelectedCookies.setAttribute("accesskey", this._bundle.getString("removeSelectedCookies.accesskey"));
+
+ this._populateList(true);
+
+ document.getElementById("filter").focus();
+ },
+
+ uninit: function() {
+ var os = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ os.removeObserver(this, "cookie-changed");
+ os.removeObserver(this, "perm-changed");
+ },
+
+ _populateList: function(aInitialLoad) {
+ this._loadCookies();
+ this._tree.view = this._view;
+ if (aInitialLoad)
+ this.sort("rawHost");
+ if (this._view.rowCount > 0)
+ this._tree.view.selection.select(0);
+
+ if (aInitialLoad) {
+ if ("arguments" in window &&
+ window.arguments[0] &&
+ window.arguments[0].filterString)
+ this.setFilter(window.arguments[0].filterString);
+ }
+ else {
+ if (document.getElementById("filter").value != "")
+ this.filter();
+ }
+
+ this._updateRemoveAllButton();
+
+ this._saveState();
+ },
+
+ _cookieEquals: function(aCookieA, aCookieB, aStrippedHost) {
+ return aCookieA.rawHost == aStrippedHost &&
+ aCookieA.name == aCookieB.name &&
+ aCookieA.path == aCookieB.path &&
+ ChromeUtils.isOriginAttributesEqual(aCookieA.originAttributes,
+ aCookieB.originAttributes);
+ },
+
+ observe: function(aCookie, aTopic, aData) {
+ if (aTopic != "cookie-changed")
+ return;
+
+ if (aCookie instanceof Components.interfaces.nsICookie) {
+ var strippedHost = this._makeStrippedHost(aCookie.host);
+ if (aData == "changed")
+ this._handleCookieChanged(aCookie, strippedHost);
+ else if (aData == "added")
+ this._handleCookieAdded(aCookie, strippedHost);
+ }
+ else if (aData == "cleared") {
+ this._hosts = {};
+ this._hostOrder = [];
+
+ var oldRowCount = this._view._rowCount;
+ this._view._rowCount = 0;
+ this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
+ this._view.selection.clearSelection();
+ this._updateRemoveAllButton();
+ }
+ else if (aData == "reload") {
+ // first, clear any existing entries
+ this.observe(aCookie, aTopic, "cleared");
+
+ // then, reload the list
+ this._populateList(false);
+ }
+
+ // We don't yet handle aData == "deleted" - it's a less common case
+ // and is rather complicated as selection tracking is difficult
+ },
+
+ _handleCookieChanged: function(changedCookie, strippedHost) {
+ var rowIndex = 0;
+ var cookieItem = null;
+ if (!this._view._filtered) {
+ for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) {
+ ++rowIndex;
+ var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host];
+ if (this._hostOrder[i] == strippedHost) { // host == strippedHost) {
+ // Host matches, look for the cookie within this Host collection
+ // and update its data
+ for (var j = 0; j < hostItem.cookies.length; ++j) {
+ ++rowIndex;
+ var currCookie = hostItem.cookies[j];
+ if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
+ currCookie.value = changedCookie.value;
+ currCookie.isSecure = changedCookie.isSecure;
+ currCookie.isDomain = changedCookie.isDomain;
+ currCookie.expires = changedCookie.expires;
+ cookieItem = currCookie;
+ break;
+ }
+ }
+ }
+ else if (hostItem.open)
+ rowIndex += hostItem.cookies.length;
+ }
+ }
+ else {
+ // Just walk the filter list to find the item. It doesn't matter that
+ // we don't update the main Host collection when we do this, because
+ // when the filter is reset the Host collection is rebuilt anyway.
+ for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) {
+ currCookie = this._view._filterSet[rowIndex];
+ if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
+ currCookie.value = changedCookie.value;
+ currCookie.isSecure = changedCookie.isSecure;
+ currCookie.isDomain = changedCookie.isDomain;
+ currCookie.expires = changedCookie.expires;
+ cookieItem = currCookie;
+ break;
+ }
+ }
+ }
+
+ // Make sure the tree display is up to date...
+ this._tree.treeBoxObject.invalidateRow(rowIndex);
+ // ... and if the cookie is selected, update the displayed metadata too
+ if (cookieItem != null && this._view.selection.currentIndex == rowIndex)
+ this._updateCookieData(cookieItem);
+ },
+
+ _handleCookieAdded: function(changedCookie, strippedHost) {
+ var rowCountImpact = 0;
+ var addedHost = { value: 0 };
+ this._addCookie(strippedHost, changedCookie, addedHost);
+ if (!this._view._filtered) {
+ // The Host collection for this cookie already exists, and it's not open,
+ // so don't increment the rowCountImpact becaues the user is not going to
+ // see the additional rows as they're hidden.
+ if (addedHost.value || this._hosts[strippedHost].open)
+ ++rowCountImpact;
+ }
+ else {
+ // We're in search mode, and the cookie being added matches
+ // the search condition, so add it to the list.
+ var c = this._makeCookieObject(strippedHost, changedCookie);
+ if (this._cookieMatchesFilter(c)) {
+ this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie));
+ ++rowCountImpact;
+ }
+ }
+ // Now update the tree display at the end (we could/should re run the sort
+ // if any to get the position correct.)
+ var oldRowCount = this._rowCount;
+ this._view._rowCount += rowCountImpact;
+ this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
+
+ this._updateRemoveAllButton();
+ },
+
+ _view: {
+ _filtered : false,
+ _filterSet : [],
+ _filterValue: "",
+ _rowCount : 0,
+ _cacheValid : 0,
+ _cacheItems : [],
+ get rowCount() {
+ return this._rowCount;
+ },
+
+ _getItemAtIndex: function(aIndex) {
+ if (this._filtered)
+ return this._filterSet[aIndex];
+
+ var start = 0;
+ var count = 0, hostIndex = 0;
+
+ var cacheIndex = Math.min(this._cacheValid, aIndex);
+ if (cacheIndex > 0) {
+ var cacheItem = this._cacheItems[cacheIndex];
+ start = cacheItem['start'];
+ count = hostIndex = cacheItem['count'];
+ }
+
+ for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
+ var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
+ if (!currHost) continue;
+ if (count == aIndex)
+ return currHost;
+ hostIndex = count;
+
+ var cacheEntry = { 'start' : i, 'count' : count };
+ var cacheStart = count;
+
+ if (currHost.open) {
+ if (count < aIndex && aIndex <= (count + currHost.cookies.length)) {
+ // We are looking for an entry within this host's children,
+ // enumerate them looking for the index.
+ ++count;
+ for (var i = 0; i < currHost.cookies.length; ++i) {
+ if (count == aIndex) {
+ var cookie = currHost.cookies[i];
+ cookie.parentIndex = hostIndex;
+ return cookie;
+ }
+ ++count;
+ }
+ }
+ else {
+ // A host entry was open, but we weren't looking for an index
+ // within that host entry's children, so skip forward over the
+ // entry's children. We need to add one to increment for the
+ // host value too.
+ count += currHost.cookies.length + 1;
+ }
+ }
+ else
+ ++count;
+
+ for (var j = cacheStart; j < count; j++)
+ this._cacheItems[j] = cacheEntry;
+ this._cacheValid = count - 1;
+ }
+ return null;
+ },
+
+ _removeItemAtIndex: function(aIndex, aCount) {
+ var removeCount = aCount === undefined ? 1 : aCount;
+ if (this._filtered) {
+ // remove the cookies from the unfiltered set so that they
+ // don't reappear when the filter is changed. See bug 410863.
+ for (var i = aIndex; i < aIndex + removeCount; ++i) {
+ var item = this._filterSet[i];
+ var parent = gCookiesWindow._hosts[item.rawHost];
+ for (var j = 0; j < parent.cookies.length; ++j) {
+ if (item == parent.cookies[j]) {
+ parent.cookies.splice(j, 1);
+ break;
+ }
+ }
+ }
+ this._filterSet.splice(aIndex, removeCount);
+ return;
+ }
+
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return;
+ this._invalidateCache(aIndex - 1);
+ if (item.container) {
+ gCookiesWindow._hosts[item.rawHost] = null;
+ } else {
+ var parent = this._getItemAtIndex(item.parentIndex);
+ for (var i = 0; i < parent.cookies.length; ++i) {
+ var cookie = parent.cookies[i];
+ if (item.rawHost == cookie.rawHost &&
+ item.name == cookie.name &&
+ item.path == cookie.path &&
+ ChromeUtils.isOriginAttributesEqual(item.originAttributes,
+ cookie.originAttributes)) {
+ parent.cookies.splice(i, removeCount);
+ }
+ }
+ }
+ },
+
+ _invalidateCache: function(aIndex) {
+ this._cacheValid = Math.min(this._cacheValid, aIndex);
+ },
+
+ getCellText: function(aIndex, aColumn) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item)
+ return "";
+ if (aColumn.id == "domainCol")
+ return item.rawHost;
+ else if (aColumn.id == "nameCol")
+ return item.name;
+ }
+ else {
+ if (aColumn.id == "domainCol")
+ return this._filterSet[aIndex].rawHost;
+ else if (aColumn.id == "nameCol")
+ return this._filterSet[aIndex].name;
+ }
+ return "";
+ },
+
+ _selection: null,
+ get selection () { return this._selection; },
+ set selection (val) { this._selection = val; return val; },
+ getRowProperties: function(aIndex) { return ""; },
+ getCellProperties: function(aIndex, aColumn) { return ""; },
+ getColumnProperties: function(aColumn) { return ""; },
+ isContainer: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return false;
+ return item.container;
+ }
+ return false;
+ },
+ isContainerOpen: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return false;
+ return item.open;
+ }
+ return false;
+ },
+ isContainerEmpty: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return false;
+ return item.cookies.length == 0;
+ }
+ return false;
+ },
+ isSeparator: function(aIndex) { return false; },
+ isSorted: function(aIndex) { return false; },
+ canDrop: function(aIndex, aOrientation) { return false; },
+ drop: function(aIndex, aOrientation) {},
+ getParentIndex: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ // If an item has no parent index (i.e. it is at the top level) this
+ // function MUST return -1 otherwise we will go into an infinite loop.
+ // Containers are always top level items in the cookies tree, so make
+ // sure to return the appropriate value here.
+ if (!item || item.container) return -1;
+ return item.parentIndex;
+ }
+ return -1;
+ },
+ hasNextSibling: function(aParentIndex, aIndex) {
+ if (!this._filtered) {
+ // |aParentIndex| appears to be bogus, but we can get the real
+ // parent index by getting the entry for |aIndex| and reading the
+ // parentIndex field.
+ // The index of the last item in this host collection is the
+ // index of the parent + the size of the host collection, and
+ // aIndex has a next sibling if it is less than this value.
+ var item = this._getItemAtIndex(aIndex);
+ if (item) {
+ if (item.container) {
+ for (var i = aIndex + 1; i < this.rowCount; ++i) {
+ var subsequent = this._getItemAtIndex(i);
+ if (subsequent.container)
+ return true;
+ }
+ return false;
+ }
+ else {
+ var parent = this._getItemAtIndex(item.parentIndex);
+ if (parent && parent.container)
+ return aIndex < item.parentIndex + parent.cookies.length;
+ }
+ }
+ }
+ return aIndex < this.rowCount - 1;
+ },
+ hasPreviousSibling: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return false;
+ var parent = this._getItemAtIndex(item.parentIndex);
+ if (parent && parent.container)
+ return aIndex > item.parentIndex + 1;
+ }
+ return aIndex > 0;
+ },
+ getLevel: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return 0;
+ return item.level;
+ }
+ return 0;
+ },
+ getImageSrc: function(aIndex, aColumn) {},
+ getProgressMode: function(aIndex, aColumn) {},
+ getCellValue: function(aIndex, aColumn) {},
+ setTree: function(aTree) {},
+ toggleOpenState: function(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) return;
+ this._invalidateCache(aIndex);
+ var multiplier = item.open ? -1 : 1;
+ var delta = multiplier * item.cookies.length;
+ this._rowCount += delta;
+ item.open = !item.open;
+ gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta);
+ gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex);
+ }
+ },
+ cycleHeader: function(aColumn) {},
+ selectionChanged: function() {},
+ cycleCell: function(aIndex, aColumn) {},
+ isEditable: function(aIndex, aColumn) {
+ return false;
+ },
+ isSelectable: function(aIndex, aColumn) {
+ return false;
+ },
+ setCellValue: function(aIndex, aColumn, aValue) {},
+ setCellText: function(aIndex, aColumn, aValue) {},
+ performAction: function(aAction) {},
+ performActionOnRow: function(aAction, aIndex) {},
+ performActionOnCell: function(aAction, aindex, aColumn) {}
+ },
+
+ _makeStrippedHost: function(aHost) {
+ var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost;
+ return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost;
+ },
+
+ _addCookie: function(aStrippedHost, aCookie, aHostCount) {
+ if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) {
+ this._hosts[aStrippedHost] = { cookies : [],
+ rawHost : aStrippedHost,
+ level : 0,
+ open : false,
+ container : true };
+ this._hostOrder.push(aStrippedHost);
+ ++aHostCount.value;
+ }
+
+ var c = this._makeCookieObject(aStrippedHost, aCookie);
+ this._hosts[aStrippedHost].cookies.push(c);
+ },
+
+ _makeCookieObject: function(aStrippedHost, aCookie) {
+ var host = aCookie.host;
+ var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host;
+ var c = { name : aCookie.name,
+ value : aCookie.value,
+ isDomain : aCookie.isDomain,
+ host : aCookie.host,
+ rawHost : aStrippedHost,
+ path : aCookie.path,
+ isSecure : aCookie.isSecure,
+ expires : aCookie.expires,
+ level : 1,
+ container : false,
+ originAttributes: aCookie.originAttributes };
+ return c;
+ },
+
+ _loadCookies: function() {
+ var e = this._cm.enumerator;
+ var hostCount = { value: 0 };
+ this._hosts = {};
+ this._hostOrder = [];
+ while (e.hasMoreElements()) {
+ var cookie = e.getNext();
+ if (cookie && cookie instanceof Components.interfaces.nsICookie) {
+ var strippedHost = this._makeStrippedHost(cookie.host);
+ this._addCookie(strippedHost, cookie, hostCount);
+ }
+ else
+ break;
+ }
+ this._view._rowCount = hostCount.value;
+ },
+
+ formatExpiresString: function(aExpires) {
+ if (aExpires) {
+ var date = new Date(1000 * aExpires);
+ return this._ds.FormatDateTime("", this._ds.dateFormatLong,
+ this._ds.timeFormatSeconds,
+ date.getFullYear(),
+ date.getMonth() + 1,
+ date.getDate(),
+ date.getHours(),
+ date.getMinutes(),
+ date.getSeconds());
+ }
+ return this._bundle.getString("expireAtEndOfSession");
+ },
+
+ _updateCookieData: function(aItem) {
+ var seln = this._view.selection;
+ var ids = ["name", "value", "host", "path", "isSecure", "expires"];
+ var properties;
+
+ if (aItem && !aItem.container && seln.count > 0) {
+ properties = { name: aItem.name, value: aItem.value, host: aItem.host,
+ path: aItem.path, expires: this.formatExpiresString(aItem.expires),
+ isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
+ : this._bundle.getString("hostColon"),
+ isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
+ : this._bundle.getString("forAnyConnection") };
+ for (var i = 0; i < ids.length; ++i)
+ document.getElementById(ids[i]).disabled = false;
+ }
+ else {
+ var noneSelected = this._bundle.getString("noCookieSelected");
+ properties = { name: noneSelected, value: noneSelected, host: noneSelected,
+ path: noneSelected, expires: noneSelected,
+ isSecure: noneSelected };
+ for (i = 0; i < ids.length; ++i)
+ document.getElementById(ids[i]).disabled = true;
+ }
+ for (var property in properties)
+ document.getElementById(property).value = properties[property];
+ },
+
+ onCookieSelected: function() {
+ var properties, item;
+ var seln = this._tree.view.selection;
+ var hasRows = this._tree.view.rowCount > 0;
+ var hasSelection = seln.count > 0;
+ if (!this._view._filtered)
+ item = this._view._getItemAtIndex(seln.currentIndex);
+ else
+ item = this._view._filterSet[seln.currentIndex];
+
+ this._updateCookieData(item);
+
+ var rangeCount = seln.getRangeCount();
+ var selectedCookieCount = 0;
+ for (var i = 0; i < rangeCount; ++i) {
+ var min = {}; var max = {};
+ seln.getRangeAt(i, min, max);
+ for (var j = min.value; j <= max.value; ++j) {
+ item = this._view._getItemAtIndex(j);
+ if (!item) continue;
+ if (item.container && !item.open)
+ selectedCookieCount += item.cookies.length;
+ else if (!item.container)
+ ++selectedCookieCount;
+ }
+ }
+ var item = this._view._getItemAtIndex(seln.currentIndex);
+ if (item && seln.count == 1 && item.container && item.open)
+ selectedCookieCount += 2;
+
+ let buttonLabel = this._bundle.getString("removeSelectedCookies.label");
+ let removeSelectedCookies = document.getElementById("removeSelectedCookies");
+ removeSelectedCookies.label = PluralForm.get(selectedCookieCount, buttonLabel)
+ .replace("#1", selectedCookieCount);
+
+ removeSelectedCookies.disabled = !hasRows || !hasSelection;
+ },
+
+ performDeletion: function(deleteItems) {
+ var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ var blockFutureCookies = false;
+ if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
+ blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
+ for (var i = 0; i < deleteItems.length; ++i) {
+ var item = deleteItems[i];
+ this._cm.remove(item.host, item.name, item.path,
+ blockFutureCookies, item.originAttributes);
+ }
+ },
+
+ deleteCookie: function() {
+ // Selection Notes
+ // - Selection always moves to *NEXT* adjacent item unless item
+ // is last child at a given level in which case it moves to *PREVIOUS*
+ // item
+ //
+ // Selection Cases (Somewhat Complicated)
+ //
+ // 1) Single cookie selected, host has single child
+ // v cnn.com
+ // //// cnn.com ///////////// goksdjf@ ////
+ // > atwola.com
+ //
+ // Before SelectedIndex: 1 Before RowCount: 3
+ // After SelectedIndex: 0 After RowCount: 1
+ //
+ // 2) Host selected, host open
+ // v goats.com ////////////////////////////
+ // goats.com sldkkfjl
+ // goat.scom flksj133
+ // > atwola.com
+ //
+ // Before SelectedIndex: 0 Before RowCount: 4
+ // After SelectedIndex: 0 After RowCount: 1
+ //
+ // 3) Host selected, host closed
+ // > goats.com ////////////////////////////
+ // > atwola.com
+ //
+ // Before SelectedIndex: 0 Before RowCount: 2
+ // After SelectedIndex: 0 After RowCount: 1
+ //
+ // 4) Single cookie selected, host has many children
+ // v goats.com
+ // goats.com sldkkfjl
+ // //// goats.com /////////// flksjl33 ////
+ // > atwola.com
+ //
+ // Before SelectedIndex: 2 Before RowCount: 4
+ // After SelectedIndex: 1 After RowCount: 3
+ //
+ // 5) Single cookie selected, host has many children
+ // v goats.com
+ // //// goats.com /////////// flksjl33 ////
+ // goats.com sldkkfjl
+ // > atwola.com
+ //
+ // Before SelectedIndex: 1 Before RowCount: 4
+ // After SelectedIndex: 1 After RowCount: 3
+ var seln = this._view.selection;
+ var tbo = this._tree.treeBoxObject;
+
+ if (seln.count < 1) return;
+
+ var nextSelected = 0;
+ var rowCountImpact = 0;
+ var deleteItems = [];
+ if (!this._view._filtered) {
+ var ci = seln.currentIndex;
+ nextSelected = ci;
+ var invalidateRow = -1;
+ var item = this._view._getItemAtIndex(ci);
+ if (item.container) {
+ rowCountImpact -= (item.open ? item.cookies.length : 0) + 1;
+ deleteItems = deleteItems.concat(item.cookies);
+ if (!this._view.hasNextSibling(-1, ci))
+ --nextSelected;
+ this._view._removeItemAtIndex(ci);
+ }
+ else {
+ var parent = this._view._getItemAtIndex(item.parentIndex);
+ --rowCountImpact;
+ if (parent.cookies.length == 1) {
+ --rowCountImpact;
+ deleteItems.push(item);
+ if (!this._view.hasNextSibling(-1, ci))
+ --nextSelected;
+ if (!this._view.hasNextSibling(-1, item.parentIndex))
+ --nextSelected;
+ this._view._removeItemAtIndex(item.parentIndex);
+ invalidateRow = item.parentIndex;
+ }
+ else {
+ deleteItems.push(item);
+ if (!this._view.hasNextSibling(-1, ci))
+ --nextSelected;
+ this._view._removeItemAtIndex(ci);
+ }
+ }
+ this._view._rowCount += rowCountImpact;
+ tbo.rowCountChanged(ci, rowCountImpact);
+ if (invalidateRow != -1)
+ tbo.invalidateRow(invalidateRow);
+ }
+ else {
+ var rangeCount = seln.getRangeCount();
+ // Traverse backwards through selections to avoid messing
+ // up the indices when they are deleted.
+ // See bug 388079.
+ for (var i = rangeCount - 1; i >= 0; --i) {
+ var min = {}; var max = {};
+ seln.getRangeAt(i, min, max);
+ nextSelected = min.value;
+ for (var j = min.value; j <= max.value; ++j) {
+ deleteItems.push(this._view._getItemAtIndex(j));
+ if (!this._view.hasNextSibling(-1, max.value))
+ --nextSelected;
+ }
+ var delta = max.value - min.value + 1;
+ this._view._removeItemAtIndex(min.value, delta);
+ rowCountImpact = -1 * delta;
+ this._view._rowCount += rowCountImpact;
+ tbo.rowCountChanged(min.value, rowCountImpact);
+ }
+ }
+
+ this.performDeletion(deleteItems);
+
+ if (nextSelected < 0)
+ seln.clearSelection();
+ else {
+ seln.select(nextSelected);
+ this._tree.focus();
+ }
+ },
+
+ deleteAllCookies: function() {
+ if (this._view._filtered) {
+ var rowCount = this._view.rowCount;
+ var deleteItems = [];
+ for (var index = 0; index < rowCount; index++) {
+ deleteItems.push(this._view._getItemAtIndex(index));
+ }
+ this._view._removeItemAtIndex(0, rowCount);
+ this._view._rowCount = 0;
+ this._tree.treeBoxObject.rowCountChanged(0, -rowCount);
+ this.performDeletion(deleteItems);
+ }
+ else {
+ this._cm.removeAll();
+ }
+ this._updateRemoveAllButton();
+ this.focusFilterBox();
+ },
+
+ onCookieKeyPress: function(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
+ this.deleteCookie();
+ }
+ },
+
+ _lastSortProperty : "",
+ _lastSortAscending: false,
+ sort: function(aProperty) {
+ var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
+ // Sort the Non-Filtered Host Collections
+ if (aProperty == "rawHost") {
+ function sortByHost(a, b) {
+ return a.toLowerCase().localeCompare(b.toLowerCase());
+ }
+ this._hostOrder.sort(sortByHost);
+ if (!ascending)
+ this._hostOrder.reverse();
+ }
+
+ function sortByProperty(a, b) {
+ return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
+ }
+ for (var host in this._hosts) {
+ var cookies = this._hosts[host].cookies;
+ cookies.sort(sortByProperty);
+ if (!ascending)
+ cookies.reverse();
+ }
+ // Sort the Filtered List, if in Filtered mode
+ if (this._view._filtered) {
+ this._view._filterSet.sort(sortByProperty);
+ if (!ascending)
+ this._view._filterSet.reverse();
+ }
+
+ // Adjust the Sort Indicator
+ var domainCol = document.getElementById("domainCol");
+ var nameCol = document.getElementById("nameCol");
+ var sortOrderString = ascending ? "ascending" : "descending";
+ if (aProperty == "rawHost") {
+ domainCol.setAttribute("sortDirection", sortOrderString);
+ nameCol.removeAttribute("sortDirection");
+ }
+ else {
+ nameCol.setAttribute("sortDirection", sortOrderString);
+ domainCol.removeAttribute("sortDirection");
+ }
+
+ this._view._invalidateCache(0);
+ this._view.selection.clearSelection();
+ if (this._view.rowCount > 0) {
+ this._view.selection.select(0);
+ }
+ this._tree.treeBoxObject.invalidate();
+ this._tree.treeBoxObject.ensureRowIsVisible(0);
+
+ this._lastSortAscending = ascending;
+ this._lastSortProperty = aProperty;
+ },
+
+ clearFilter: function() {
+ // Revert to single-select in the tree
+ this._tree.setAttribute("seltype", "single");
+
+ // Clear the Tree Display
+ this._view._filtered = false;
+ this._view._rowCount = 0;
+ this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
+ this._view._filterSet = [];
+
+ // Just reload the list to make sure deletions are respected
+ this._loadCookies();
+ this._tree.view = this._view;
+
+ // Restore sort order
+ var sortby = this._lastSortProperty;
+ if (sortby == "") {
+ this._lastSortAscending = false;
+ this.sort("rawHost");
+ }
+ else {
+ this._lastSortAscending = !this._lastSortAscending;
+ this.sort(sortby);
+ }
+
+ // Restore open state
+ for (var i = 0; i < this._openIndices.length; ++i)
+ this._view.toggleOpenState(this._openIndices[i]);
+ this._openIndices = [];
+
+ // Restore selection
+ this._view.selection.clearSelection();
+ for (i = 0; i < this._lastSelectedRanges.length; ++i) {
+ var range = this._lastSelectedRanges[i];
+ this._view.selection.rangedSelect(range.min, range.max, true);
+ }
+ this._lastSelectedRanges = [];
+
+ document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
+ this._updateRemoveAllButton();
+ },
+
+ _cookieMatchesFilter: function(aCookie) {
+ return aCookie.rawHost.indexOf(this._view._filterValue) != -1 ||
+ aCookie.name.indexOf(this._view._filterValue) != -1 ||
+ aCookie.value.indexOf(this._view._filterValue) != -1;
+ },
+
+ _filterCookies: function(aFilterValue) {
+ this._view._filterValue = aFilterValue;
+ var cookies = [];
+ for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) {
+ var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
+ if (!currHost) continue;
+ for (var j = 0; j < currHost.cookies.length; ++j) {
+ var cookie = currHost.cookies[j];
+ if (this._cookieMatchesFilter(cookie))
+ cookies.push(cookie);
+ }
+ }
+ return cookies;
+ },
+
+ _lastSelectedRanges: [],
+ _openIndices: [],
+ _saveState: function() {
+ // Save selection
+ var seln = this._view.selection;
+ this._lastSelectedRanges = [];
+ var rangeCount = seln.getRangeCount();
+ for (var i = 0; i < rangeCount; ++i) {
+ var min = {}; var max = {};
+ seln.getRangeAt(i, min, max);
+ this._lastSelectedRanges.push({ min: min.value, max: max.value });
+ }
+
+ // Save open states
+ this._openIndices = [];
+ for (i = 0; i < this._view.rowCount; ++i) {
+ var item = this._view._getItemAtIndex(i);
+ if (item && item.container && item.open)
+ this._openIndices.push(i);
+ }
+ },
+
+ _updateRemoveAllButton: function() {
+ let removeAllCookies = document.getElementById("removeAllCookies");
+ removeAllCookies.disabled = this._view._rowCount == 0;
+
+ let labelStringID = "removeAllCookies.label";
+ let accessKeyStringID = "removeAllCookies.accesskey";
+ if (this._view._filtered) {
+ labelStringID = "removeAllShownCookies.label";
+ accessKeyStringID = "removeAllShownCookies.accesskey";
+ }
+ removeAllCookies.setAttribute("label", this._bundle.getString(labelStringID));
+ removeAllCookies.setAttribute("accesskey", this._bundle.getString(accessKeyStringID));
+ },
+
+ filter: function() {
+ var filter = document.getElementById("filter").value;
+ if (filter == "") {
+ gCookiesWindow.clearFilter();
+ return;
+ }
+ var view = gCookiesWindow._view;
+ view._filterSet = gCookiesWindow._filterCookies(filter);
+ if (!view._filtered) {
+ // Save Display Info for the Non-Filtered mode when we first
+ // enter Filtered mode.
+ gCookiesWindow._saveState();
+ view._filtered = true;
+ }
+ // Move to multi-select in the tree
+ gCookiesWindow._tree.setAttribute("seltype", "multiple");
+
+ // Clear the display
+ var oldCount = view._rowCount;
+ view._rowCount = 0;
+ gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
+ // Set up the filtered display
+ view._rowCount = view._filterSet.length;
+ gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
+
+ // if the view is not empty then select the first item
+ if (view.rowCount > 0)
+ view.selection.select(0);
+
+ document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
+ this._updateRemoveAllButton();
+ },
+
+ setFilter: function(aFilterString) {
+ document.getElementById("filter").value = aFilterString;
+ this.filter();
+ },
+
+ focusFilterBox: function() {
+ var filter = document.getElementById("filter");
+ filter.focus();
+ filter.select();
+ },
+
+ onWindowKeyPress: function(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
+ window.close();
+ }
+};
diff --git a/browser/components/preferences/cookies.xul b/browser/components/preferences/cookies.xul
new file mode 100644
index 000000000..8dd757fd0
--- /dev/null
+++ b/browser/components/preferences/cookies.xul
@@ -0,0 +1,103 @@
+<?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"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/cookies.dtd" >
+
+<window id="CookiesDialog" windowtype="Browser:Cookies"
+ class="windowDialog" title="&window.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: &window.width;;"
+ onload="gCookiesWindow.init();"
+ onunload="gCookiesWindow.uninit();"
+ persist="screenX screenY width height"
+ onkeypress="gCookiesWindow.onWindowKeyPress(event);">
+
+ <script src="chrome://browser/content/preferences/cookies.js"/>
+
+ <stringbundle id="bundlePreferences"
+ src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <keyset>
+ <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
+ <key key="&focusSearch1.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
+ <key key="&focusSearch2.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
+ </keyset>
+
+ <vbox flex="1" class="contentPane">
+ <hbox align="center">
+ <label accesskey="&filter.accesskey;" control="filter">&filter.label;</label>
+ <textbox type="search" id="filter" flex="1"
+ aria-controls="cookiesList"
+ oncommand="gCookiesWindow.filter();"/>
+ </hbox>
+ <separator class="thin"/>
+ <label control="cookiesList" id="cookiesIntro" value="&cookiesonsystem.label;"/>
+ <separator class="thin"/>
+ <tree id="cookiesList" flex="1" style="height: 10em;"
+ onkeypress="gCookiesWindow.onCookieKeyPress(event)"
+ onselect="gCookiesWindow.onCookieSelected();"
+ hidecolumnpicker="true" seltype="single">
+ <treecols>
+ <treecol id="domainCol" label="&cookiedomain.label;" flex="2" primary="true"
+ persist="width" onclick="gCookiesWindow.sort('rawHost');"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="nameCol" label="&cookiename.label;" flex="1"
+ persist="width"
+ onclick="gCookiesWindow.sort('name');"/>
+ </treecols>
+ <treechildren id="cookiesChildren"/>
+ </tree>
+ <hbox id="cookieInfoBox">
+ <grid flex="1" id="cookieInfoGrid">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <hbox pack="end"><label id="nameLabel" control="name" value="&props.name.label;"/></hbox>
+ <textbox id="name" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="valueLabel" control="value" value="&props.value.label;"/></hbox>
+ <textbox id="value" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="isDomain" control="host" value="&props.domain.label;"/></hbox>
+ <textbox id="host" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="pathLabel" control="path" value="&props.path.label;"/></hbox>
+ <textbox id="path" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="isSecureLabel" control="isSecure" value="&props.secure.label;"/></hbox>
+ <textbox id="isSecure" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="expiresLabel" control="expires" value="&props.expires.label;"/></hbox>
+ <textbox id="expires" readonly="true" class="plain"/>
+ </row>
+ </rows>
+ </grid>
+ </hbox>
+ </vbox>
+ <hbox align="end">
+ <hbox class="actionButtons" flex="1">
+ <button id="removeSelectedCookies" disabled="true" icon="clear"
+ oncommand="gCookiesWindow.deleteCookie();"/>
+ <button id="removeAllCookies" disabled="true" icon="clear"
+ oncommand="gCookiesWindow.deleteAllCookies();"/>
+ <spacer flex="1"/>
+ <button oncommand="close();" icon="close"
+ label="&button.close.label;" accesskey="&button.close.accesskey;"/>
+ </hbox>
+ <resizer type="window" dir="bottomend"/>
+ </hbox>
+</window>
diff --git a/browser/components/preferences/fonts.js b/browser/components/preferences/fonts.js
new file mode 100644
index 000000000..975671a6e
--- /dev/null
+++ b/browser/components/preferences/fonts.js
@@ -0,0 +1,143 @@
+// 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/.
+
+// browser.display.languageList LOCK ALL when LOCKED
+
+const kDefaultFontType = "font.default.%LANG%";
+const kFontNameFmtSerif = "font.name.serif.%LANG%";
+const kFontNameFmtSansSerif = "font.name.sans-serif.%LANG%";
+const kFontNameFmtMonospace = "font.name.monospace.%LANG%";
+const kFontNameListFmtSerif = "font.name-list.serif.%LANG%";
+const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
+const kFontNameListFmtMonospace = "font.name-list.monospace.%LANG%";
+const kFontSizeFmtVariable = "font.size.variable.%LANG%";
+const kFontSizeFmtFixed = "font.size.fixed.%LANG%";
+const kFontMinSizeFmt = "font.minimum-size.%LANG%";
+
+var gFontsDialog = {
+ _selectLanguageGroup: function (aLanguageGroup)
+ {
+ var prefs = [{ format: kDefaultFontType, type: "string", element: "defaultFontType", fonttype: null},
+ { format: kFontNameFmtSerif, type: "fontname", element: "serif", fonttype: "serif" },
+ { format: kFontNameFmtSansSerif, type: "fontname", element: "sans-serif", fonttype: "sans-serif" },
+ { format: kFontNameFmtMonospace, type: "fontname", element: "monospace", fonttype: "monospace" },
+ { format: kFontNameListFmtSerif, type: "unichar", element: null, fonttype: "serif" },
+ { format: kFontNameListFmtSansSerif, type: "unichar", element: null, fonttype: "sans-serif" },
+ { format: kFontNameListFmtMonospace, type: "unichar", element: null, fonttype: "monospace" },
+ { format: kFontSizeFmtVariable, type: "int", element: "sizeVar", fonttype: null },
+ { format: kFontSizeFmtFixed, type: "int", element: "sizeMono", fonttype: null },
+ { format: kFontMinSizeFmt, type: "int", element: "minSize", fonttype: null }];
+ var preferences = document.getElementById("fontPreferences");
+ for (var i = 0; i < prefs.length; ++i) {
+ var preference = document.getElementById(prefs[i].format.replace(/%LANG%/, aLanguageGroup));
+ if (!preference) {
+ preference = document.createElement("preference");
+ var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup);
+ preference.id = name;
+ preference.setAttribute("name", name);
+ preference.setAttribute("type", prefs[i].type);
+ preferences.appendChild(preference);
+ }
+
+ if (!prefs[i].element)
+ continue;
+
+ var element = document.getElementById(prefs[i].element);
+ if (element) {
+ element.setAttribute("preference", preference.id);
+
+ if (prefs[i].fonttype)
+ FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element);
+
+ preference.setElementValue(element);
+ }
+ }
+ },
+
+ readFontLanguageGroup: function ()
+ {
+ var languagePref = document.getElementById("font.language.group");
+ this._selectLanguageGroup(languagePref.value);
+ return undefined;
+ },
+
+ readFontSelection: function (aElement)
+ {
+ // Determine the appropriate value to select, for the following cases:
+ // - there is no setting
+ // - the font selected by the user is no longer present (e.g. deleted from
+ // fonts folder)
+ var preference = document.getElementById(aElement.getAttribute("preference"));
+ if (preference.value) {
+ var fontItems = aElement.getElementsByAttribute("value", preference.value);
+
+ // There is a setting that actually is in the list. Respect it.
+ if (fontItems.length > 0)
+ return undefined;
+ }
+
+ var defaultValue = aElement.firstChild.firstChild.getAttribute("value");
+ var languagePref = document.getElementById("font.language.group");
+ preference = document.getElementById("font.name-list." + aElement.id + "." + languagePref.value);
+ if (!preference || !preference.hasUserValue)
+ return defaultValue;
+
+ var fontNames = preference.value.split(",");
+ var stripWhitespace = /^\s*(.*)\s*$/;
+
+ for (var i = 0; i < fontNames.length; ++i) {
+ var fontName = fontNames[i].replace(stripWhitespace, "$1");
+ fontItems = aElement.getElementsByAttribute("value", fontName);
+ if (fontItems.length)
+ break;
+ }
+ if (fontItems.length)
+ return fontItems[0].getAttribute("value");
+ return defaultValue;
+ },
+
+ readUseDocumentFonts: function ()
+ {
+ var preference = document.getElementById("browser.display.use_document_fonts");
+ return preference.value == 1;
+ },
+
+ writeUseDocumentFonts: function ()
+ {
+ var useDocumentFonts = document.getElementById("useDocumentFonts");
+ return useDocumentFonts.checked ? 1 : 0;
+ },
+
+ onBeforeAccept: function ()
+ {
+ // Only care in in-content prefs
+ if (!window.frameElement) {
+ return true;
+ }
+
+ let preferences = document.querySelectorAll("preference[id*='font.minimum-size']");
+ // It would be good if we could avoid touching languages the pref pages won't use, but
+ // unfortunately the language group APIs (deducing language groups from language codes)
+ // are C++ - only. So we just check all the things the user touched:
+ // Don't care about anything up to 24px, or if this value is the same as set previously:
+ preferences = Array.filter(preferences, prefEl => {
+ return prefEl.value > 24 && prefEl.value != prefEl.valueFromPreferences;
+ });
+ if (!preferences.length) {
+ return;
+ }
+
+ let strings = document.getElementById("bundlePreferences");
+ let title = strings.getString("veryLargeMinimumFontTitle");
+ let confirmLabel = strings.getString("acceptVeryLargeMinimumFont");
+ let warningMessage = strings.getString("veryLargeMinimumFontWarning");
+ let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {});
+ let flags = Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL |
+ Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING |
+ Services.prompt.BUTTON_POS_1_DEFAULT;
+ let buttonChosen = Services.prompt.confirmEx(window, title, warningMessage, flags, confirmLabel, null, "", "", {});
+ return buttonChosen == 0;
+ },
+};
+
diff --git a/browser/components/preferences/fonts.xul b/browser/components/preferences/fonts.xul
new file mode 100644
index 000000000..1c14bcf91
--- /dev/null
+++ b/browser/components/preferences/fonts.xul
@@ -0,0 +1,275 @@
+<?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 prefwindow SYSTEM "chrome://browser/locale/preferences/fonts.dtd" >
+
+<prefwindow id="FontsDialog" type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&fontsDialog.title;"
+ dlgbuttons="accept,cancel,help"
+ ondialoghelp="openPrefsHelp()"
+ onbeforeaccept="return gFontsDialog.onBeforeAccept();"
+ style="">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+
+ <prefpane id="FontsDialogPane"
+ class="largeDialogContainer"
+ helpTopic="prefs-fonts-and-colors">
+
+ <preferences id="fontPreferences">
+ <preference id="font.language.group" name="font.language.group" type="wstring"/>
+ <preference id="browser.display.use_document_fonts"
+ name="browser.display.use_document_fonts"
+ type="int"/>
+ <preference id="intl.charset.fallback.override" name="intl.charset.fallback.override" type="string"/>
+ </preferences>
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+ <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/>
+ <script type="application/javascript" src="chrome://browser/content/preferences/fonts.js"/>
+
+ <!-- Fonts for: [ Language ] -->
+ <groupbox>
+ <caption>
+ <hbox align="center">
+ <label accesskey="&language.accesskey;" control="selectLangs">&language.label;</label>
+ </hbox>
+ <menulist id="selectLangs" preference="font.language.group"
+ onsyncfrompreference="return gFontsDialog.readFontLanguageGroup();">
+ <menupopup>
+ <menuitem value="ar" label="&font.langGroup.arabic;"/>
+ <menuitem value="x-armn" label="&font.langGroup.armenian;"/>
+ <menuitem value="x-beng" label="&font.langGroup.bengali;"/>
+ <menuitem value="zh-CN" label="&font.langGroup.simpl-chinese;"/>
+ <menuitem value="zh-HK" label="&font.langGroup.trad-chinese-hk;"/>
+ <menuitem value="zh-TW" label="&font.langGroup.trad-chinese;"/>
+ <menuitem value="x-cyrillic" label="&font.langGroup.cyrillic;"/>
+ <menuitem value="x-devanagari" label="&font.langGroup.devanagari;"/>
+ <menuitem value="x-ethi" label="&font.langGroup.ethiopic;"/>
+ <menuitem value="x-geor" label="&font.langGroup.georgian;"/>
+ <menuitem value="el" label="&font.langGroup.el;"/>
+ <menuitem value="x-gujr" label="&font.langGroup.gujarati;"/>
+ <menuitem value="x-guru" label="&font.langGroup.gurmukhi;"/>
+ <menuitem value="he" label="&font.langGroup.hebrew;"/>
+ <menuitem value="ja" label="&font.langGroup.japanese;"/>
+ <menuitem value="x-knda" label="&font.langGroup.kannada;"/>
+ <menuitem value="x-khmr" label="&font.langGroup.khmer;"/>
+ <menuitem value="ko" label="&font.langGroup.korean;"/>
+ <menuitem value="x-western" label="&font.langGroup.latin;"/>
+ <menuitem value="x-mlym" label="&font.langGroup.malayalam;"/>
+ <menuitem value="x-orya" label="&font.langGroup.oriya;"/>
+ <menuitem value="x-sinh" label="&font.langGroup.sinhala;"/>
+ <menuitem value="x-tamil" label="&font.langGroup.tamil;"/>
+ <menuitem value="x-telu" label="&font.langGroup.telugu;"/>
+ <menuitem value="th" label="&font.langGroup.thai;"/>
+ <menuitem value="x-tibt" label="&font.langGroup.tibetan;"/>
+ <menuitem value="x-cans" label="&font.langGroup.canadian;"/>
+ <menuitem value="x-unicode" label="&font.langGroup.other;"/>
+ </menupopup>
+ </menulist>
+ </caption>
+
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ <column/>
+ <column/>
+ </columns>
+
+ <rows>
+ <row>
+ <separator class="thin"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label accesskey="&proportional.accesskey;" control="defaultFontType">&proportional.label;</label>
+ </hbox>
+ <menulist id="defaultFontType" flex="1" style="width: 0px;">
+ <menupopup>
+ <menuitem value="serif" label="&useDefaultFontSerif.label;"/>
+ <menuitem value="sans-serif" label="&useDefaultFontSansSerif.label;"/>
+ </menupopup>
+ </menulist>
+ <hbox align="center" pack="end">
+ <label value="&size.label;"
+ accesskey="&sizeProportional.accesskey;"
+ control="sizeVar"/>
+ </hbox>
+ <menulist id="sizeVar">
+ <menupopup>
+ <menuitem value="9" label="9"/>
+ <menuitem value="10" label="10"/>
+ <menuitem value="11" label="11"/>
+ <menuitem value="12" label="12"/>
+ <menuitem value="13" label="13"/>
+ <menuitem value="14" label="14"/>
+ <menuitem value="15" label="15"/>
+ <menuitem value="16" label="16"/>
+ <menuitem value="17" label="17"/>
+ <menuitem value="18" label="18"/>
+ <menuitem value="20" label="20"/>
+ <menuitem value="22" label="22"/>
+ <menuitem value="24" label="24"/>
+ <menuitem value="26" label="26"/>
+ <menuitem value="28" label="28"/>
+ <menuitem value="30" label="30"/>
+ <menuitem value="32" label="32"/>
+ <menuitem value="34" label="34"/>
+ <menuitem value="36" label="36"/>
+ <menuitem value="40" label="40"/>
+ <menuitem value="44" label="44"/>
+ <menuitem value="48" label="48"/>
+ <menuitem value="56" label="56"/>
+ <menuitem value="64" label="64"/>
+ <menuitem value="72" label="72"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label accesskey="&serif.accesskey;" control="serif">&serif.label;</label>
+ </hbox>
+ <menulist id="serif" flex="1" style="width: 0px;"
+ onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('serif'));"/>
+ <spacer/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label accesskey="&sans-serif.accesskey;" control="sans-serif">&sans-serif.label;</label>
+ </hbox>
+ <menulist id="sans-serif" flex="1" style="width: 0px;"
+ onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('sans-serif'));"/>
+ <spacer/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label accesskey="&monospace.accesskey;" control="monospace">&monospace.label;</label>
+ </hbox>
+ <menulist id="monospace" flex="1" style="width: 0px;" crop="right"
+ onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('monospace'));"/>
+ <hbox align="center" pack="end">
+ <label value="&size.label;"
+ accesskey="&sizeMonospace.accesskey;"
+ control="sizeMono"/>
+ </hbox>
+ <menulist id="sizeMono">
+ <menupopup>
+ <menuitem value="9" label="9"/>
+ <menuitem value="10" label="10"/>
+ <menuitem value="11" label="11"/>
+ <menuitem value="12" label="12"/>
+ <menuitem value="13" label="13"/>
+ <menuitem value="14" label="14"/>
+ <menuitem value="15" label="15"/>
+ <menuitem value="16" label="16"/>
+ <menuitem value="17" label="17"/>
+ <menuitem value="18" label="18"/>
+ <menuitem value="20" label="20"/>
+ <menuitem value="22" label="22"/>
+ <menuitem value="24" label="24"/>
+ <menuitem value="26" label="26"/>
+ <menuitem value="28" label="28"/>
+ <menuitem value="30" label="30"/>
+ <menuitem value="32" label="32"/>
+ <menuitem value="34" label="34"/>
+ <menuitem value="36" label="36"/>
+ <menuitem value="40" label="40"/>
+ <menuitem value="44" label="44"/>
+ <menuitem value="48" label="48"/>
+ <menuitem value="56" label="56"/>
+ <menuitem value="64" label="64"/>
+ <menuitem value="72" label="72"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ <separator class="thin"/>
+ <hbox flex="1">
+ <spacer flex="1"/>
+ <hbox align="center" pack="end">
+ <label accesskey="&minSize.accesskey;" control="minSize">&minSize.label;</label>
+ <menulist id="minSize">
+ <menupopup>
+ <menuitem value="0" label="&minSize.none;"/>
+ <menuitem value="9" label="9"/>
+ <menuitem value="10" label="10"/>
+ <menuitem value="11" label="11"/>
+ <menuitem value="12" label="12"/>
+ <menuitem value="13" label="13"/>
+ <menuitem value="14" label="14"/>
+ <menuitem value="15" label="15"/>
+ <menuitem value="16" label="16"/>
+ <menuitem value="17" label="17"/>
+ <menuitem value="18" label="18"/>
+ <menuitem value="20" label="20"/>
+ <menuitem value="22" label="22"/>
+ <menuitem value="24" label="24"/>
+ <menuitem value="26" label="26"/>
+ <menuitem value="28" label="28"/>
+ <menuitem value="30" label="30"/>
+ <menuitem value="32" label="32"/>
+ <menuitem value="34" label="34"/>
+ <menuitem value="36" label="36"/>
+ <menuitem value="40" label="40"/>
+ <menuitem value="44" label="44"/>
+ <menuitem value="48" label="48"/>
+ <menuitem value="56" label="56"/>
+ <menuitem value="64" label="64"/>
+ <menuitem value="72" label="72"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ <separator/>
+ <separator class="groove"/>
+ <hbox>
+ <checkbox id="useDocumentFonts"
+ label="&allowPagesToUse.label;" accesskey="&allowPagesToUse.accesskey;"
+ preference="browser.display.use_document_fonts"
+ onsyncfrompreference="return gFontsDialog.readUseDocumentFonts();"
+ onsynctopreference="return gFontsDialog.writeUseDocumentFonts();"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Character Encoding -->
+ <groupbox>
+ <caption label="&languages.customize.Fallback.grouplabel;"/>
+ <description>&languages.customize.Fallback.desc;</description>
+ <hbox align="center">
+ <label value="&languages.customize.Fallback.label;"
+ accesskey="&languages.customize.Fallback.accesskey;"
+ control="DefaultCharsetList"/>
+ <menulist id="DefaultCharsetList" preference="intl.charset.fallback.override">
+ <menupopup>
+ <menuitem label="&languages.customize.Fallback.auto;" value="*"/>
+ <menuitem label="&languages.customize.Fallback.utf8;" value="UTF-8"/>
+ <menuitem label="&languages.customize.Fallback.arabic;" value="windows-1256"/>
+ <menuitem label="&languages.customize.Fallback.baltic;" value="windows-1257"/>
+ <menuitem label="&languages.customize.Fallback.ceiso;" value="ISO-8859-2"/>
+ <menuitem label="&languages.customize.Fallback.cewindows;" value="windows-1250"/>
+ <menuitem label="&languages.customize.Fallback.simplified;" value="gbk"/>
+ <menuitem label="&languages.customize.Fallback.traditional;" value="Big5"/>
+ <menuitem label="&languages.customize.Fallback.cyrillic;" value="windows-1251"/>
+ <menuitem label="&languages.customize.Fallback.greek;" value="ISO-8859-7"/>
+ <menuitem label="&languages.customize.Fallback.hebrew;" value="windows-1255"/>
+ <menuitem label="&languages.customize.Fallback.japanese;" value="Shift_JIS"/>
+ <menuitem label="&languages.customize.Fallback.korean;" value="EUC-KR"/>
+ <menuitem label="&languages.customize.Fallback.thai;" value="windows-874"/>
+ <menuitem label="&languages.customize.Fallback.turkish;" value="windows-1254"/>
+ <menuitem label="&languages.customize.Fallback.vietnamese;" value="windows-1258"/>
+ <menuitem label="&languages.customize.Fallback.other;" value="windows-1252"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</prefwindow>
diff --git a/browser/components/preferences/handlers.css b/browser/components/preferences/handlers.css
new file mode 100644
index 000000000..9a1d47446
--- /dev/null
+++ b/browser/components/preferences/handlers.css
@@ -0,0 +1,25 @@
+/* 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/. */
+
+richlistitem {
+ -moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler");
+}
+
+richlistitem[selected="true"] {
+ -moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler-selected");
+}
+
+/**
+ * Make the icons appear.
+ * Note: we display the icon box for every item whether or not it has an icon
+ * so the labels of all the items align vertically.
+ */
+.actionsMenu > menupopup > menuitem > .menu-iconic-left {
+ display: -moz-box;
+ min-width: 16px;
+}
+
+listitem.offlineapp {
+ -moz-binding: url("chrome://browser/content/preferences/handlers.xml#offlineapp");
+}
diff --git a/browser/components/preferences/handlers.xml b/browser/components/preferences/handlers.xml
new file mode 100644
index 000000000..5fb915cee
--- /dev/null
+++ b/browser/components/preferences/handlers.xml
@@ -0,0 +1,81 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/applications.dtd">
+ %brandDTD;
+ %applicationsDTD;
+]>
+
+<bindings id="handlerBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="handler-base" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <implementation>
+ <property name="type" readonly="true">
+ <getter>
+ return this.getAttribute("type");
+ </getter>
+ </property>
+ </implementation>
+ </binding>
+
+ <binding id="handler" extends="chrome://browser/content/preferences/handlers.xml#handler-base">
+ <content>
+ <xul:hbox flex="1" equalsize="always">
+ <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
+ <xul:image src="moz-icon://goat?size=16" class="typeIcon"
+ xbl:inherits="src=typeIcon" height="16" width="16"/>
+ <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/>
+ </xul:hbox>
+ <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=actionDescription">
+ <xul:image xbl:inherits="src=actionIcon" height="16" width="16" class="actionIcon"/>
+ <xul:label flex="1" crop="end" xbl:inherits="value=actionDescription"/>
+ </xul:hbox>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="handler-selected" extends="chrome://browser/content/preferences/handlers.xml#handler-base">
+ <content>
+ <xul:hbox flex="1" equalsize="always">
+ <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
+ <xul:image src="moz-icon://goat?size=16" class="typeIcon"
+ xbl:inherits="src=typeIcon" height="16" width="16"/>
+ <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/>
+ </xul:hbox>
+ <xul:hbox flex="1">
+ <xul:menulist class="actionsMenu" flex="1" crop="end" selectedIndex="1"
+ xbl:inherits="tooltiptext=actionDescription"
+ oncommand="gApplicationsPane.onSelectAction(event.originalTarget)">
+ <xul:menupopup/>
+ </xul:menulist>
+ </xul:hbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ gApplicationsPane.rebuildActionsMenu();
+ </constructor>
+ </implementation>
+
+ </binding>
+
+ <binding id="offlineapp"
+ extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <children>
+ <xul:listcell xbl:inherits="label=origin"/>
+ <xul:listcell xbl:inherits="label=usage"/>
+ </children>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/browser/components/preferences/jar.mn b/browser/components/preferences/jar.mn
new file mode 100644
index 000000000..9256e3927
--- /dev/null
+++ b/browser/components/preferences/jar.mn
@@ -0,0 +1,44 @@
+# 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/.
+
+browser.jar:
+* content/browser/preferences/advanced.xul
+* content/browser/preferences/advanced.js
+ content/browser/preferences/applications.xul
+* content/browser/preferences/applications.js
+ content/browser/preferences/applicationManager.xul
+ content/browser/preferences/applicationManager.js
+* content/browser/preferences/colors.xul
+ content/browser/preferences/cookies.xul
+ content/browser/preferences/cookies.js
+* content/browser/preferences/content.xul
+ content/browser/preferences/content.js
+ content/browser/preferences/connection.xul
+ content/browser/preferences/connection.js
+ content/browser/preferences/fonts.xul
+ content/browser/preferences/fonts.js
+ content/browser/preferences/handlers.xml
+ content/browser/preferences/handlers.css
+ content/browser/preferences/languages.xul
+ content/browser/preferences/languages.js
+* content/browser/preferences/main.xul
+* content/browser/preferences/main.js
+ content/browser/preferences/newtaburl.js
+ content/browser/preferences/permissions.xul
+ content/browser/preferences/permissions.js
+* content/browser/preferences/preferences.xul
+ content/browser/preferences/privacy.xul
+ content/browser/preferences/privacy.js
+ content/browser/preferences/sanitize.xul
+ content/browser/preferences/sanitize.js
+ content/browser/preferences/security.xul
+ content/browser/preferences/security.js
+ content/browser/preferences/selectBookmark.xul
+ content/browser/preferences/selectBookmark.js
+#ifdef MOZ_SERVICES_SYNC
+ content/browser/preferences/sync.xul
+ content/browser/preferences/sync.js
+#endif
+* content/browser/preferences/tabs.xul
+* content/browser/preferences/tabs.js
diff --git a/browser/components/preferences/languages.js b/browser/components/preferences/languages.js
new file mode 100644
index 000000000..5b8ea38a6
--- /dev/null
+++ b/browser/components/preferences/languages.js
@@ -0,0 +1,303 @@
+// 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 gLanguagesDialog = {
+
+ _availableLanguagesList : [],
+ _acceptLanguages : { },
+
+ _selectedItemID : null,
+
+ init: function ()
+ {
+ if (!this._availableLanguagesList.length)
+ this._loadAvailableLanguages();
+ },
+
+ get _activeLanguages()
+ {
+ return document.getElementById("activeLanguages");
+ },
+
+ get _availableLanguages()
+ {
+ return document.getElementById("availableLanguages");
+ },
+
+ _loadAvailableLanguages: function ()
+ {
+ // This is a parser for: resource://gre/res/language.properties
+ // The file is formatted like so:
+ // ab[-cd].accept=true|false
+ // ab = language
+ // cd = region
+ var bundleAccepted = document.getElementById("bundleAccepted");
+ var bundleRegions = document.getElementById("bundleRegions");
+ var bundleLanguages = document.getElementById("bundleLanguages");
+ var bundlePreferences = document.getElementById("bundlePreferences");
+
+ function LanguageInfo(aName, aABCD, aIsVisible)
+ {
+ this.name = aName;
+ this.abcd = aABCD;
+ this.isVisible = aIsVisible;
+ }
+
+ // 1) Read the available languages out of language.properties
+ var strings = bundleAccepted.strings;
+ while (strings.hasMoreElements()) {
+ var currString = strings.getNext();
+ if (!(currString instanceof Components.interfaces.nsIPropertyElement))
+ break;
+
+ var property = currString.key.split("."); // ab[-cd].accept
+ if (property[1] == "accept") {
+ var abCD = property[0];
+ var abCDPairs = abCD.split("-"); // ab[-cd]
+ var useABCDFormat = abCDPairs.length > 1;
+ var ab = useABCDFormat ? abCDPairs[0] : abCD;
+ var cd = useABCDFormat ? abCDPairs[1] : "";
+ if (ab) {
+ var language = "";
+ try {
+ language = bundleLanguages.getString(ab);
+ }
+ catch (e) { continue; };
+
+ var region = "";
+ if (useABCDFormat) {
+ try {
+ region = bundleRegions.getString(cd);
+ }
+ catch (e) { continue; }
+ }
+
+ var name = "";
+ if (useABCDFormat)
+ name = bundlePreferences.getFormattedString("languageRegionCodeFormat",
+ [language, region, abCD]);
+ else
+ name = bundlePreferences.getFormattedString("languageCodeFormat",
+ [language, abCD]);
+
+ if (name && abCD) {
+ var isVisible = currString.value == "true" &&
+ (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD]);
+ var li = new LanguageInfo(name, abCD, isVisible);
+ this._availableLanguagesList.push(li);
+ }
+ }
+ }
+ }
+ this._buildAvailableLanguageList();
+ },
+
+ _buildAvailableLanguageList: function ()
+ {
+ var availableLanguagesPopup = document.getElementById("availableLanguagesPopup");
+ while (availableLanguagesPopup.hasChildNodes())
+ availableLanguagesPopup.removeChild(availableLanguagesPopup.firstChild);
+
+ // Sort the list of languages by name
+ this._availableLanguagesList.sort(function (a, b) {
+ return a.name.localeCompare(b.name);
+ });
+
+ // Load the UI with the data
+ for (var i = 0; i < this._availableLanguagesList.length; ++i) {
+ var abCD = this._availableLanguagesList[i].abcd;
+ if (this._availableLanguagesList[i].isVisible &&
+ (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD])) {
+ var menuitem = document.createElement("menuitem");
+ menuitem.id = this._availableLanguagesList[i].abcd;
+ availableLanguagesPopup.appendChild(menuitem);
+ menuitem.setAttribute("label", this._availableLanguagesList[i].name);
+ }
+ }
+ },
+
+ readAcceptLanguages: function ()
+ {
+ while (this._activeLanguages.hasChildNodes())
+ this._activeLanguages.removeChild(this._activeLanguages.firstChild);
+
+ var selectedIndex = 0;
+ var preference = document.getElementById("intl.accept_languages");
+ if (preference.value == "")
+ return undefined;
+ var languages = preference.value.toLowerCase().split(/\s*,\s*/);
+ for (var i = 0; i < languages.length; ++i) {
+ var name = this._getLanguageName(languages[i]);
+ if (!name)
+ name = "[" + languages[i] + "]";
+ var listitem = document.createElement("listitem");
+ listitem.id = languages[i];
+ if (languages[i] == this._selectedItemID)
+ selectedIndex = i;
+ this._activeLanguages.appendChild(listitem);
+ listitem.setAttribute("label", name);
+
+ // Hash this language as an "Active" language so we don't
+ // show it in the list that can be added.
+ this._acceptLanguages[languages[i]] = true;
+ }
+
+ if (this._activeLanguages.childNodes.length > 0) {
+ this._activeLanguages.ensureIndexIsVisible(selectedIndex);
+ this._activeLanguages.selectedIndex = selectedIndex;
+ }
+
+ return undefined;
+ },
+
+ writeAcceptLanguages: function ()
+ {
+ return undefined;
+ },
+
+ onAvailableLanguageSelect: function ()
+ {
+ var addButton = document.getElementById("addButton");
+ addButton.disabled = false;
+
+ this._availableLanguages.removeAttribute("accesskey");
+ },
+
+ addLanguage: function ()
+ {
+ var selectedID = this._availableLanguages.selectedItem.id;
+ var preference = document.getElementById("intl.accept_languages");
+ var arrayOfPrefs = preference.value.toLowerCase().split(/\s*,\s*/);
+ for (var i = 0; i < arrayOfPrefs.length; ++i ){
+ if (arrayOfPrefs[i] == selectedID)
+ return;
+ }
+
+ this._selectedItemID = selectedID;
+
+ if (preference.value == "")
+ preference.value = selectedID;
+ else {
+ arrayOfPrefs.unshift(selectedID);
+ preference.value = arrayOfPrefs.join(",");
+ }
+
+ this._acceptLanguages[selectedID] = true;
+ this._availableLanguages.selectedItem = null;
+
+ // Rebuild the available list with the added item removed...
+ this._buildAvailableLanguageList();
+
+ this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("label2"));
+ },
+
+ removeLanguage: function ()
+ {
+ // Build the new preference value string.
+ var languagesArray = [];
+ for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
+ var item = this._activeLanguages.childNodes[i];
+ if (!item.selected)
+ languagesArray.push(item.id);
+ else
+ this._acceptLanguages[item.id] = false;
+ }
+ var string = languagesArray.join(",");
+
+ // Get the item to select after the remove operation completes.
+ var selection = this._activeLanguages.selectedItems;
+ var lastSelected = selection[selection.length-1];
+ var selectItem = lastSelected.nextSibling || lastSelected.previousSibling;
+ selectItem = selectItem ? selectItem.id : null;
+
+ this._selectedItemID = selectItem;
+
+ // Update the preference and force a UI rebuild
+ var preference = document.getElementById("intl.accept_languages");
+ preference.value = string;
+
+ this._buildAvailableLanguageList();
+ },
+
+ _getLanguageName: function (aABCD)
+ {
+ if (!this._availableLanguagesList.length)
+ this._loadAvailableLanguages();
+ for (var i = 0; i < this._availableLanguagesList.length; ++i) {
+ if (aABCD == this._availableLanguagesList[i].abcd)
+ return this._availableLanguagesList[i].name;
+ }
+ return "";
+ },
+
+ moveUp: function ()
+ {
+ var selectedItem = this._activeLanguages.selectedItems[0];
+ var previousItem = selectedItem.previousSibling;
+
+ var string = "";
+ for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
+ var item = this._activeLanguages.childNodes[i];
+ string += (i == 0 ? "" : ",");
+ if (item.id == previousItem.id)
+ string += selectedItem.id;
+ else if (item.id == selectedItem.id)
+ string += previousItem.id;
+ else
+ string += item.id;
+ }
+
+ this._selectedItemID = selectedItem.id;
+
+ // Update the preference and force a UI rebuild
+ var preference = document.getElementById("intl.accept_languages");
+ preference.value = string;
+ },
+
+ moveDown: function ()
+ {
+ var selectedItem = this._activeLanguages.selectedItems[0];
+ var nextItem = selectedItem.nextSibling;
+
+ var string = "";
+ for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
+ var item = this._activeLanguages.childNodes[i];
+ string += (i == 0 ? "" : ",");
+ if (item.id == nextItem.id)
+ string += selectedItem.id;
+ else if (item.id == selectedItem.id)
+ string += nextItem.id;
+ else
+ string += item.id;
+ }
+
+ this._selectedItemID = selectedItem.id;
+
+ // Update the preference and force a UI rebuild
+ var preference = document.getElementById("intl.accept_languages");
+ preference.value = string;
+ },
+
+ onLanguageSelect: function ()
+ {
+ var upButton = document.getElementById("up");
+ var downButton = document.getElementById("down");
+ var removeButton = document.getElementById("remove");
+ switch (this._activeLanguages.selectedCount) {
+ case 0:
+ upButton.disabled = downButton.disabled = removeButton.disabled = true;
+ break;
+ case 1:
+ upButton.disabled = this._activeLanguages.selectedIndex == 0;
+ downButton.disabled = this._activeLanguages.selectedIndex == this._activeLanguages.childNodes.length - 1;
+ removeButton.disabled = false;
+ break;
+ default:
+ upButton.disabled = true;
+ downButton.disabled = true;
+ removeButton.disabled = false;
+ }
+ }
+};
+
diff --git a/browser/components/preferences/languages.xul b/browser/components/preferences/languages.xul
new file mode 100644
index 000000000..bd74e11cf
--- /dev/null
+++ b/browser/components/preferences/languages.xul
@@ -0,0 +1,94 @@
+<?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/. -->
+
+<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/languages.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<prefwindow id="LanguagesDialog" type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&languages.customize.Header;"
+ dlgbuttons="accept,cancel,help"
+ ondialoghelp="openPrefsHelp()"
+ style="width: &window.width;;">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+
+ <prefpane id="LanguagesDialogPane"
+ onpaneload="gLanguagesDialog.init();"
+ helpTopic="prefs-languages">
+
+ <preferences>
+ <preference id="intl.accept_languages" name="intl.accept_languages" type="wstring"/>
+ <preference id="pref.browser.language.disable_button.up"
+ name="pref.browser.language.disable_button.up"
+ type="bool"/>
+ <preference id="pref.browser.language.disable_button.down"
+ name="pref.browser.language.disable_button.down"
+ type="bool"/>
+ <preference id="pref.browser.language.disable_button.remove"
+ name="pref.browser.language.disable_button.remove"
+ type="bool"/>
+ </preferences>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/languages.js"/>
+
+ <stringbundleset id="languageSet">
+ <stringbundle id="bundleRegions" src="chrome://global/locale/regionNames.properties"/>
+ <stringbundle id="bundleLanguages" src="chrome://global/locale/languageNames.properties"/>
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+ <stringbundle id="bundleAccepted" src="resource://gre/res/language.properties"/>
+ </stringbundleset>
+
+ <description>&languages.customize.prefLangDescript;</description>
+ <label>&languages.customize.active.label;</label>
+ <grid flex="1">
+ <columns>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows>
+ <row flex="1">
+ <listbox id="activeLanguages" flex="1" rows="6"
+ seltype="multiple" onselect="gLanguagesDialog.onLanguageSelect();"
+ preference="intl.accept_languages"
+ onsyncfrompreference="return gLanguagesDialog.readAcceptLanguages();"
+ onsynctopreference="return gLanguagesDialog.writeAcceptLanguages();"/>
+ <vbox>
+ <button id="up" class="up" oncommand="gLanguagesDialog.moveUp();" disabled="true"
+ label="&languages.customize.moveUp.label;"
+ accesskey="&languages.customize.moveUp.accesskey;"
+ preference="pref.browser.language.disable_button.up"/>
+ <button id="down" class="down" oncommand="gLanguagesDialog.moveDown();" disabled="true"
+ label="&languages.customize.moveDown.label;"
+ accesskey="&languages.customize.moveDown.accesskey;"
+ preference="pref.browser.language.disable_button.down"/>
+ <button id="remove" oncommand="gLanguagesDialog.removeLanguage();" disabled="true"
+ label="&languages.customize.deleteButton.label;"
+ accesskey="&languages.customize.deleteButton.accesskey;"
+ preference="pref.browser.language.disable_button.remove"/>
+ </vbox>
+ </row>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row>
+ <menulist id="availableLanguages" oncommand="gLanguagesDialog.onAvailableLanguageSelect();"
+ label="&languages.customize.selectLanguage.label;"
+ label2="&languages.customize.selectLanguage.label;">
+ <menupopup id="availableLanguagesPopup"/>
+ </menulist>
+ <button id="addButton" oncommand="gLanguagesDialog.addLanguage();" disabled="true"
+ label="&languages.customize.addButton.label;"
+ accesskey="&languages.customize.addButton.accesskey;"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ <separator/>
+ </prefpane>
+</prefwindow>
+
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
new file mode 100644
index 000000000..d4daeeab3
--- /dev/null
+++ b/browser/components/preferences/main.js
@@ -0,0 +1,543 @@
+// 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/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+ "resource:///modules/DownloadsCommon.jsm");
+
+var gMainPane = {
+ _pane: null,
+
+ /**
+ * Initialization of this.
+ */
+ init: function ()
+ {
+ this._pane = document.getElementById("paneMain");
+
+ // set up the "use current page" label-changing listener
+ this._updateUseCurrentButton();
+ window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
+
+ this.updateBrowserStartupLastSession();
+
+ this.setupDownloadsWindowOptions();
+
+#ifdef HAVE_SHELL_SERVICE
+ this.updateSetDefaultBrowser();
+#ifdef XP_WIN
+ // In Windows 8 we launch the control panel since it's the only
+ // way to get all file type association prefs. So we don't know
+ // when the user will select the default. We refresh here periodically
+ // in case the default changes. On other Windows OS's defaults can also
+ // be set while the prefs are open.
+ window.setInterval(this.updateSetDefaultBrowser, 1000);
+#endif
+#endif
+
+ // Notify observers that the UI is now ready
+ Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .notifyObservers(window, "main-pane-loaded", null);
+ },
+
+ setupDownloadsWindowOptions: function ()
+ {
+ let showWhenDownloading = document.getElementById("showWhenDownloading");
+ let closeWhenDone = document.getElementById("closeWhenDone");
+
+ // These radio buttons should be hidden when the Downloads Panel is enabled.
+ let shouldHide = !DownloadsCommon.useToolkitUI;
+ showWhenDownloading.hidden = shouldHide;
+ closeWhenDone.hidden = shouldHide;
+ },
+
+ // HOME PAGE
+
+ /*
+ * Preferences:
+ *
+ * browser.startup.homepage
+ * - the user's home page, as a string; if the home page is a set of tabs,
+ * this will be those URLs separated by the pipe character "|"
+ * browser.startup.page
+ * - what page(s) to show when the user starts the application, as an integer:
+ *
+ * 0: a blank page
+ * 1: the home page (as set by the browser.startup.homepage pref)
+ * 2: the last page the user visited (DEPRECATED)
+ * 3: windows and tabs from the last session (a.k.a. session restore)
+ *
+ * The deprecated option is not exposed in UI; however, if the user has it
+ * selected and doesn't change the UI for this preference, the deprecated
+ * option is preserved.
+ */
+
+ syncFromHomePref: function ()
+ {
+ let homePref = document.getElementById("browser.startup.homepage");
+
+ // If the pref is set to about:home, set the value to "" to show the
+ // placeholder text (about:home title).
+ if (homePref.value.toLowerCase() == "about:home")
+ return "";
+
+ // If the pref is actually "", show a blank page. The actual home page
+ // loading code treats them the same, and we don't want the placeholder text
+ // to be shown.
+ if (homePref.value == "")
+ return "about:logopage";
+
+ // Otherwise, show the actual pref value.
+ return undefined;
+ },
+
+ syncToHomePref: function (value)
+ {
+ // If the value is "", use about:home.
+ if (value == "")
+ return "about:home";
+
+ // Otherwise, use the actual textbox value.
+ return undefined;
+ },
+
+ /**
+ * Sets the home page to the current displayed page (or frontmost tab, if the
+ * most recent browser window contains multiple tabs), updating preference
+ * window UI to reflect this.
+ */
+ setHomePageToCurrent: function ()
+ {
+ let homePage = document.getElementById("browser.startup.homepage");
+ let tabs = this._getTabsForHomePage();
+ function getTabURI(t) t.linkedBrowser.currentURI.spec;
+
+ // FIXME Bug 244192: using dangerous "|" joiner!
+ if (tabs.length)
+ homePage.value = tabs.map(getTabURI).join("|");
+ },
+
+ /**
+ * Displays a dialog in which the user can select a bookmark to use as home
+ * page. If the user selects a bookmark, that bookmark's name is displayed in
+ * UI and the bookmark's address is stored to the home page preference.
+ */
+ setHomePageToBookmark: function ()
+ {
+ var rv = { urls: null, names: null };
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/selectBookmark.xul",
+ "resizable", rv);
+ if (rv.urls && rv.names) {
+ var homePage = document.getElementById("browser.startup.homepage");
+
+ // XXX still using dangerous "|" joiner!
+ homePage.value = rv.urls.join("|");
+ }
+ },
+
+ /**
+ * Switches the "Use Current Page" button between its singular and plural
+ * forms.
+ */
+ _updateUseCurrentButton: function () {
+ let useCurrent = document.getElementById("useCurrent");
+
+ let tabs = this._getTabsForHomePage();
+ if (tabs.length > 1)
+ useCurrent.label = useCurrent.getAttribute("label2");
+ else
+ useCurrent.label = useCurrent.getAttribute("label1");
+
+ // In this case, the button's disabled state is set by preferences.xml.
+ if (document.getElementById
+ ("pref.browser.homepage.disable_button.current_page").locked)
+ return;
+
+ useCurrent.disabled = !tabs.length
+ },
+
+ _getTabsForHomePage: function ()
+ {
+ var win;
+ var tabs = [];
+ if (document.documentElement.instantApply) {
+ const Cc = Components.classes, Ci = Components.interfaces;
+ // If we're in instant-apply mode, use the most recent browser window
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator);
+ win = wm.getMostRecentWindow("navigator:browser");
+ }
+ else {
+ win = window.opener;
+ }
+
+ if (win && win.document.documentElement
+ .getAttribute("windowtype") == "navigator:browser") {
+ // We should only include visible & non-pinned tabs
+ tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
+ }
+
+ return tabs;
+ },
+
+ /**
+ * Restores the default home page as the user's home page.
+ */
+ restoreDefaultHomePage: function ()
+ {
+ var homePage = document.getElementById("browser.startup.homepage");
+ homePage.value = homePage.defaultValue;
+ },
+
+ // DOWNLOADS
+
+ /*
+ * Preferences:
+ *
+ * browser.download.showWhenStarting - bool
+ * True if the Download Manager should be opened when a download is
+ * started, false if it shouldn't be opened.
+ * browser.download.closeWhenDone - bool
+ * True if the Download Manager should be closed when all downloads
+ * complete, false if it should be left open.
+ * browser.download.useDownloadDir - bool
+ * True - Save files directly to the folder configured via the
+ * browser.download.folderList preference.
+ * False - Always ask the user where to save a file and default to
+ * browser.download.lastDir when displaying a folder picker dialog.
+ * browser.download.dir - local file handle
+ * A local folder the user may have selected for downloaded files to be
+ * saved. Migration of other browser settings may also set this path.
+ * This folder is enabled when folderList equals 2.
+ * browser.download.lastDir - local file handle
+ * May contain the last folder path accessed when the user browsed
+ * via the file save-as dialog. (see contentAreaUtils.js)
+ * browser.download.folderList - int
+ * Indicates the location users wish to save downloaded files too.
+ * It is also used to display special file labels when the default
+ * download location is either the Desktop or the Downloads folder.
+ * Values:
+ * 0 - The desktop is the default download location.
+ * 1 - The system's downloads folder is the default download location.
+ * 2 - The default download location is elsewhere as specified in
+ * browser.download.dir.
+ * browser.download.downloadDir
+ * deprecated.
+ * browser.download.defaultFolder
+ * deprecated.
+ */
+
+ /**
+ * Updates preferences which depend upon the value of the preference which
+ * determines whether the Downloads manager is opened at the start of a
+ * download.
+ */
+ readShowDownloadsWhenStarting: function ()
+ {
+ this.showDownloadsWhenStartingPrefChanged();
+
+ // don't override the preference's value in UI
+ return undefined;
+ },
+
+ /**
+ * Enables or disables the "close Downloads manager when downloads finished"
+ * preference element, consequently updating the associated UI.
+ */
+ showDownloadsWhenStartingPrefChanged: function ()
+ {
+ var showWhenStartingPref = document.getElementById("browser.download.manager.showWhenStarting");
+ var closeWhenDonePref = document.getElementById("browser.download.manager.closeWhenDone");
+ closeWhenDonePref.disabled = !showWhenStartingPref.value;
+ },
+
+ /**
+ * Enables/disables the folder field and Browse button based on whether a
+ * default download directory is being used.
+ */
+ readUseDownloadDir: function ()
+ {
+ var downloadFolder = document.getElementById("downloadFolder");
+ var chooseFolder = document.getElementById("chooseFolder");
+ var preference = document.getElementById("browser.download.useDownloadDir");
+ downloadFolder.disabled = !preference.value;
+ chooseFolder.disabled = !preference.value;
+
+ // don't override the preference's value in UI
+ return undefined;
+ },
+
+ /**
+ * Displays a file picker in which the user can choose the location where
+ * downloads are automatically saved, updating preferences and UI in
+ * response to the choice, if one is made.
+ */
+ chooseFolder: function ()
+ {
+ const nsIFilePicker = Components.interfaces.nsIFilePicker;
+ const nsILocalFile = Components.interfaces.nsILocalFile;
+
+ let bundlePreferences = document.getElementById("bundlePreferences");
+ let title = bundlePreferences.getString("chooseDownloadFolderTitle");
+ let folderListPref = document.getElementById("browser.download.folderList");
+ let currentDirPref = this._indexToFolder(folderListPref.value); // file
+ let defDownloads = this._indexToFolder(1); // file
+ let fp = Components.classes["@mozilla.org/filepicker;1"].
+ createInstance(nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult == nsIFilePicker.returnOK) {
+ let file = fp.file.QueryInterface(nsILocalFile);
+ let downloadDirPref = document.getElementById("browser.download.dir");
+
+ downloadDirPref.value = file;
+ folderListPref.value = this._folderToIndex(file);
+ // Note, the real prefs will not be updated yet, so dnld manager's
+ // userDownloadsDirectory may not return the right folder after
+ // this code executes. displayDownloadDirPref will be called on
+ // the assignment above to update the UI.
+ }
+ }.bind(this);
+
+ fp.init(window, title, nsIFilePicker.modeGetFolder);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ // First try to open what's currently configured
+ if (currentDirPref && currentDirPref.exists()) {
+ fp.displayDirectory = currentDirPref;
+ } // Try the system's download dir
+ else if (defDownloads && defDownloads.exists()) {
+ fp.displayDirectory = defDownloads;
+ } // Fall back to Desktop
+ else {
+ fp.displayDirectory = this._indexToFolder(0);
+ }
+ fp.open(fpCallback);
+ },
+
+ /**
+ * Initializes the download folder display settings based on the user's
+ * preferences.
+ */
+ displayDownloadDirPref: function ()
+ {
+ var folderListPref = document.getElementById("browser.download.folderList");
+ var bundlePreferences = document.getElementById("bundlePreferences");
+ var downloadFolder = document.getElementById("downloadFolder");
+ var currentDirPref = document.getElementById("browser.download.dir");
+
+ // Used in defining the correct path to the folder icon.
+ var ios = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+ var fph = ios.getProtocolHandler("file")
+ .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+ var iconUrlSpec;
+
+ // Display a 'pretty' label or the path in the UI.
+ if (folderListPref.value == 2) {
+ // Custom path selected and is configured
+ downloadFolder.label = this._getDisplayNameOfFile(currentDirPref.value);
+ iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value);
+ } else if (folderListPref.value == 1) {
+ // 'Downloads'
+ // In 1.5, this pointed to a folder we created called 'My Downloads'
+ // and was available as an option in the 1.5 drop down. On XP this
+ // was in My Documents, on OSX it was in User Docs. In 2.0, we did
+ // away with the drop down option, although the special label was
+ // still supported for the folder if it existed. Because it was
+ // not exposed it was rarely used.
+ // With 3.0, a new desktop folder - 'Downloads' was introduced for
+ // platforms and versions that don't support a default system downloads
+ // folder. See nsDownloadManager for details.
+ downloadFolder.label = bundlePreferences.getString("downloadsFolderName");
+ iconUrlSpec = fph.getURLSpecFromFile(this._indexToFolder(1));
+ } else {
+ // 'Desktop'
+ downloadFolder.label = bundlePreferences.getString("desktopFolderName");
+ iconUrlSpec = fph.getURLSpecFromFile(this._getDownloadsFolder("Desktop"));
+ }
+ downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16";
+
+ // don't override the preference's value in UI
+ return undefined;
+ },
+
+ /**
+ * Returns the textual path of a folder in readable form.
+ */
+ _getDisplayNameOfFile: function (aFolder)
+ {
+ // TODO: would like to add support for 'Downloads on Macintosh HD'
+ // for OS X users.
+ return aFolder ? aFolder.path : "";
+ },
+
+ /**
+ * Returns the Downloads folder. If aFolder is "Desktop", then the Downloads
+ * folder returned is the desktop folder; otherwise, it is a folder whose name
+ * indicates that it is a download folder and whose path is as determined by
+ * the XPCOM directory service via the download manager's attribute
+ * defaultDownloadsDirectory.
+ *
+ * @throws if aFolder is not "Desktop" or "Downloads"
+ */
+ _getDownloadsFolder: function (aFolder)
+ {
+ switch (aFolder) {
+ case "Desktop":
+ var fileLoc = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties);
+ return fileLoc.get("Desk", Components.interfaces.nsILocalFile);
+ break;
+ case "Downloads":
+ var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
+ .getService(Components.interfaces.nsIDownloadManager);
+ return dnldMgr.defaultDownloadsDirectory;
+ break;
+ }
+ throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'";
+ },
+
+ /**
+ * Determines the type of the given folder.
+ *
+ * @param aFolder
+ * the folder whose type is to be determined
+ * @returns integer
+ * 0 if aFolder is the Desktop or is unspecified,
+ * 1 if aFolder is the Downloads folder,
+ * 2 otherwise
+ */
+ _folderToIndex: function (aFolder)
+ {
+ if (!aFolder || aFolder.equals(this._getDownloadsFolder("Desktop")))
+ return 0;
+ else if (aFolder.equals(this._getDownloadsFolder("Downloads")))
+ return 1;
+ return 2;
+ },
+
+ /**
+ * Converts an integer into the corresponding folder.
+ *
+ * @param aIndex
+ * an integer
+ * @returns the Desktop folder if aIndex == 0,
+ * the Downloads folder if aIndex == 1,
+ * the folder stored in browser.download.dir
+ */
+ _indexToFolder: function (aIndex)
+ {
+ switch (aIndex) {
+ case 0:
+ return this._getDownloadsFolder("Desktop");
+ case 1:
+ return this._getDownloadsFolder("Downloads");
+ }
+ var currentDirPref = document.getElementById("browser.download.dir");
+ return currentDirPref.value;
+ },
+
+ /**
+ * Returns the value for the browser.download.folderList preference.
+ */
+ getFolderListPref: function ()
+ {
+ var folderListPref = document.getElementById("browser.download.folderList");
+ switch (folderListPref.value) {
+ case 0: // Desktop
+ case 1: // Downloads
+ return folderListPref.value;
+ break;
+ case 2: // Custom
+ var currentDirPref = document.getElementById("browser.download.dir");
+ if (currentDirPref.value) {
+ // Resolve to a known location if possible. We are writing out
+ // to prefs on this call, so now would be a good time to do it.
+ return this._folderToIndex(currentDirPref.value);
+ }
+ return 0;
+ break;
+ }
+ },
+
+ /**
+ * Hide/show the "Show my windows and tabs from last time" option based
+ * on the value of the browser.privatebrowsing.autostart pref.
+ */
+ updateBrowserStartupLastSession: function()
+ {
+ let pbAutoStartPref = document.getElementById("browser.privatebrowsing.autostart");
+ let startupPref = document.getElementById("browser.startup.page");
+ let menu = document.getElementById("browserStartupPage");
+ let option = document.getElementById("browserStartupLastSession");
+ if (pbAutoStartPref.value) {
+ option.setAttribute("disabled", "true");
+ if (option.selected) {
+ menu.selectedItem = document.getElementById("browserStartupHomePage");
+ }
+ } else {
+ option.removeAttribute("disabled");
+ startupPref.updateElements(); // select the correct index in the startup menulist
+ }
+ }
+#ifdef HAVE_SHELL_SERVICE
+ ,
+
+ // SYSTEM DEFAULTS
+
+ /*
+ * Preferences:
+ *
+ * browser.shell.checkDefault
+ * - true if a default-browser check (and prompt to make it so if necessary)
+ * occurs at startup, false otherwise
+ */
+
+ /**
+ * Show button for setting browser as default browser or information that
+ * browser is already the default browser.
+ */
+ updateSetDefaultBrowser: function()
+ {
+ let shellSvc = getShellService();
+ let setDefaultPane = document.getElementById("setDefaultPane");
+ if (!shellSvc) {
+ setDefaultPane.hidden = true;
+ document.getElementById("alwaysCheckDefault").disabled = true;
+ return;
+ }
+ let selectedIndex =
+ shellSvc.isDefaultBrowser(false, true) ? 1 : 0;
+ setDefaultPane.selectedIndex = selectedIndex;
+ },
+
+ /**
+ * Set browser as the operating system default browser.
+ */
+ setDefaultBrowser: function()
+ {
+ let shellSvc = getShellService();
+ if (!shellSvc)
+ return;
+ try {
+ let claimAllTypes = true;
+#ifdef XP_WIN
+ // In Windows 8+, the UI for selecting default protocol is much
+ // nicer than the UI for setting file type associations. So we
+ // only show the protocol association screen on Windows 8+.
+ // Windows 8 is version 6.2.
+ let version = Services.sysinfo.getProperty("version");
+ claimAllTypes = (parseFloat(version) < 6.2);
+#endif
+ shellSvc.setDefaultBrowser(claimAllTypes, false);
+ } catch (ex) {
+ Cu.reportError(ex);
+ return;
+ }
+ let selectedIndex =
+ shellSvc.isDefaultBrowser(false, true) ? 1 : 0;
+ document.getElementById("setDefaultPane").selectedIndex = selectedIndex;
+ }
+#endif
+};
diff --git a/browser/components/preferences/main.xul b/browser/components/preferences/main.xul
new file mode 100644
index 000000000..0943e5580
--- /dev/null
+++ b/browser/components/preferences/main.xul
@@ -0,0 +1,216 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
+ <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
+ %brandDTD;
+ %mainDTD;
+ %aboutHomeDTD;
+]>
+
+<overlay id="MainPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneMain"
+ onpaneload="gMainPane.init();"
+ helpTopic="prefs-main">
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/main.js"/>
+
+ <preferences id="mainPreferences">
+ <!-- XXX Button preferences -->
+
+ <!-- Startup -->
+ <preference id="browser.startup.page"
+ name="browser.startup.page"
+ type="int"/>
+ <preference id="browser.startup.homepage"
+ name="browser.startup.homepage"
+ type="wstring"/>
+
+ <preference id="pref.browser.homepage.disable_button.current_page"
+ name="pref.browser.homepage.disable_button.current_page"
+ type="bool"/>
+ <preference id="pref.browser.homepage.disable_button.bookmark_page"
+ name="pref.browser.homepage.disable_button.bookmark_page"
+ type="bool"/>
+ <preference id="pref.browser.homepage.disable_button.restore_default"
+ name="pref.browser.homepage.disable_button.restore_default"
+ type="bool"/>
+
+ <preference id="browser.privatebrowsing.autostart"
+ name="browser.privatebrowsing.autostart"
+ type="bool"
+ onchange="gMainPane.updateBrowserStartupLastSession();"/>
+
+ <!-- Downloads -->
+ <preference id="browser.download.manager.showWhenStarting"
+ name="browser.download.manager.showWhenStarting"
+ type="bool"
+ onchange="gMainPane.showDownloadsWhenStartingPrefChanged();"/>
+ <preference id="browser.download.manager.closeWhenDone"
+ name="browser.download.manager.closeWhenDone"
+ type="bool"/>
+ <preference id="browser.download.useDownloadDir"
+ name="browser.download.useDownloadDir"
+ type="bool"/>
+ <preference id="browser.download.dir"
+ name="browser.download.dir"
+ type="file"
+ onchange="gMainPane.displayDownloadDirPref();"/>
+ <preference id="browser.download.folderList" name="browser.download.folderList" type="int"/>
+ <preference id="browser.download.useToolkitUI" name="browser.download.useToolkitUI" type="bool" />
+#ifdef XP_WIN
+ <preference id="browser.download.saveZoneInformation" name="browser.download.saveZoneInformation" type="int" />
+#endif
+
+#ifdef HAVE_SHELL_SERVICE
+ <!-- System Defaults -->
+ <preference id="browser.shell.checkDefaultBrowser"
+ name="browser.shell.checkDefaultBrowser"
+ type="bool"/>
+
+ <preference id="pref.general.disable_button.default_browser"
+ name="pref.general.disable_button.default_browser"
+ type="bool"/>
+#endif
+ </preferences>
+
+#ifdef HAVE_SHELL_SERVICE
+ <stringbundle id="bundleShell" src="chrome://browser/locale/shellservice.properties"/>
+ <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
+#endif
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <!-- Startup -->
+ <groupbox id="startupGroup">
+ <caption label="&startup.label;"/>
+
+ <hbox align="center">
+ <label value="&startupPage.label;" accesskey="&startupPage.accesskey;"
+ control="browserStartupPage"/>
+ <menulist id="browserStartupPage" preference="browser.startup.page">
+ <menupopup>
+ <menuitem label="&startupHomePage.label;" value="1" id="browserStartupHomePage"/>
+ <menuitem label="&startupBlankPage.label;" value="0" id="browserStartupBlank"/>
+ <menuitem label="&startupLastSession.label;" value="3" id="browserStartupLastSession"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="center">
+ <label value="&homepage.label;" accesskey="&homepage.accesskey;" control="browserHomePage"/>
+ <textbox id="browserHomePage" class="padded uri-element" flex="1"
+ type="autocomplete" autocompletesearch="history"
+ onsyncfrompreference="return gMainPane.syncFromHomePref();"
+ onsynctopreference="return gMainPane.syncToHomePref(this.value);"
+ oninput="gNewtabUrl.writeNewtabUrl(null, this.value);"
+ placeholder="&abouthome.pageTitle;"
+ preference="browser.startup.homepage"/>
+ </hbox>
+ <hbox align="center" pack="end">
+ <button label="" accesskey="&useCurrentPage.accesskey;"
+ label1="&useCurrentPage.label;"
+ label2="&useMultiple.label;"
+ oncommand="gMainPane.setHomePageToCurrent(); gNewtabUrl.writeNewtabUrl();"
+ id="useCurrent"
+ preference="pref.browser.homepage.disable_button.current_page"/>
+ <button label="&chooseBookmark.label;" accesskey="&chooseBookmark.accesskey;"
+ oncommand="gMainPane.setHomePageToBookmark(); gNewtabUrl.writeNewtabUrl();"
+ id="useBookmark"
+ preference="pref.browser.homepage.disable_button.bookmark_page"/>
+ <button label="&restoreDefault.label;" accesskey="&restoreDefault.accesskey;"
+ oncommand="gMainPane.restoreDefaultHomePage(); gNewtabUrl.writeNewtabUrl();"
+ id="restoreDefaultHomePage"
+ preference="pref.browser.homepage.disable_button.restore_default"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Downloads -->
+ <groupbox id="downloadsGroup">
+ <caption label="&downloads.label;"/>
+
+ <checkbox id="showWhenDownloading" label="&showWhenDownloading.label;"
+ accesskey="&showWhenDownloading.accesskey;"
+ preference="browser.download.manager.showWhenStarting"
+ onsyncfrompreference="return gMainPane.readShowDownloadsWhenStarting();"/>
+ <checkbox id="closeWhenDone" label="&closeWhenDone.label;"
+ accesskey="&closeWhenDone.accesskey;" class="indent"
+ preference="browser.download.manager.closeWhenDone"/>
+
+ <separator class="thin"/>
+
+ <radiogroup id="saveWhere"
+ preference="browser.download.useDownloadDir"
+ onsyncfrompreference="return gMainPane.readUseDownloadDir();">
+ <hbox id="saveToRow">
+ <radio id="saveTo" value="true"
+ label="&saveTo.label;"
+ accesskey="&saveTo.accesskey;"
+ aria-labelledby="saveTo downloadFolder"/>
+ <filefield id="downloadFolder" flex="1"
+ preference="browser.download.folderList"
+ preference-editable="true"
+ aria-labelledby="saveTo"
+ onsyncfrompreference="return gMainPane.displayDownloadDirPref();"
+ onsynctopreference="return gMainPane.getFolderListPref()"/>
+ <button id="chooseFolder" oncommand="gMainPane.chooseFolder();"
+ accesskey="&chooseFolderWin.accesskey;"
+ label="&chooseFolderWin.label;"
+ preference="browser.download.folderList"
+ onsynctopreference="return gMainPane.getFolderListPref();"/>
+ </hbox>
+ <radio id="alwaysAsk" value="false"
+ label="&alwaysAsk.label;"
+ accesskey="&alwaysAsk.accesskey;"/>
+ </radiogroup>
+#if 0
+<!-- Disabled for now -- ToolkitUI DM is nonfunctional. -->
+ <checkbox id="classicDownloadWindow"
+ preference="browser.download.useToolkitUI"
+ label="&toolkit.classic.download.window.label;" />
+#endif
+#ifdef XP_WIN
+ <hbox align="center">
+ <label id="zoneInfoLabel" control="zoneInfo-menu">&zoneInfo.label;</label>
+ <menulist id="zoneInfo-menu"
+ preference="browser.download.saveZoneInformation"
+ sizetopopup="always">
+ <menupopup>
+ <menuitem label="&zoneInfo.never;" value="0" />
+ <menuitem label="&zoneInfo.always;" value="1" />
+ <menuitem label="&zoneInfo.system;" value="2" />
+ </menupopup>
+ </menulist>
+ </hbox>
+#endif
+ </groupbox>
+
+#ifdef HAVE_SHELL_SERVICE
+ <!-- System Defaults -->
+ <groupbox id="systemDefaultsGroup" orient="vertical">
+ <caption label="&systemDefaults.label;"/>
+
+ <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
+ label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
+ flex="1"/>
+ <hbox class="indent">
+ <deck id="setDefaultPane">
+ <button id="setDefaultButton"
+ label="&setDefault.label;" accesskey="&setDefault.accesskey;"
+ oncommand="gMainPane.setDefaultBrowser();"
+ preference="pref.general.disable_button.default_browser"/>
+ <description>&isDefault.label;</description>
+ </deck>
+ </hbox>
+ </groupbox>
+#endif
+ </prefpane>
+
+</overlay>
diff --git a/browser/components/preferences/moz.build b/browser/components/preferences/moz.build
new file mode 100644
index 000000000..c888607f0
--- /dev/null
+++ b/browser/components/preferences/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+
+for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'):
+ DEFINES[var] = CONFIG[var]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
+ DEFINES['HAVE_SHELL_SERVICE'] = 1
+
+JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file
diff --git a/browser/components/preferences/newtaburl.js b/browser/components/preferences/newtaburl.js
new file mode 100644
index 000000000..f9103f00d
--- /dev/null
+++ b/browser/components/preferences/newtaburl.js
@@ -0,0 +1,102 @@
+// 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 gNewtabUrl = {
+ /**
+ * Writes browser.newtab.url with the appropriate value.
+ * If the choice is "my home page", get and sanitize
+ * the browser home page URL to make it suitable for newtab use.
+ *
+ * Called from prefwindow ondialogaccept in preferences.xul,
+ * newtabPage oncommand in tabs.xul, browserHomePage oninput,
+ * useCurrent, useBookmark and restoreDefaultHomePage oncommand
+ * in main.xul to consider instantApply.
+ */
+ writeNewtabUrl: function(newtabUrlChoice, browserHomepageUrl) {
+ try {
+ if (newtabUrlChoice) {
+ if (Services.prefs.getBoolPref("browser.preferences.instantApply")) {
+ newtabUrlChoice = parseInt(newtabUrlChoice);
+ } else {
+ return;
+ }
+ } else {
+ if (this.newtabUrlChoiceIsSet) {
+ newtabUrlChoice = Services.prefs.getIntPref("browser.newtab.choice");
+ } else {
+ newtabUrlChoice = this.getNewtabChoice();
+ }
+ }
+ if (browserHomepageUrl || browserHomepageUrl == "") {
+ if (Services.prefs.getBoolPref("browser.preferences.instantApply")) {
+ if (browserHomepageUrl == "") {
+ browserHomepageUrl = "about:home";
+ }
+ } else {
+ return;
+ }
+ } else {
+ browserHomepageUrl = Services.prefs.getComplexValue("browser.startup.homepage",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ }
+ let newtabUrlPref = Services.prefs.getCharPref("browser.newtab.url");
+ switch (newtabUrlChoice) {
+ case 1:
+ newtabUrlPref = "about:logopage";
+ break;
+ case 2:
+ newtabUrlPref = Services.prefs.getDefaultBranch("browser.")
+ .getComplexValue("startup.homepage",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ break;
+ case 3:
+ // If url is a pipe-delimited set of pages, just take the first one.
+ let newtabUrlSanitizedPref=browserHomepageUrl.split("|")[0];
+ // XXX: do we need extra sanitation here, e.g. for invalid URLs?
+ Services.prefs.setCharPref("browser.newtab.myhome", newtabUrlSanitizedPref);
+ newtabUrlPref = newtabUrlSanitizedPref;
+ break;
+ case 4:
+ newtabUrlPref = "about:newtab";
+ break;
+ default:
+ // In case of any other value it's a custom URL, consider instantApply.
+ if (this.newtabPageCustom) {
+ newtabUrlPref = this.newtabPageCustom;
+ }
+ }
+ Services.prefs.setCharPref("browser.newtab.url",newtabUrlPref);
+ } catch(e) { console.error(e); }
+ },
+
+ /**
+ * Determines the value of browser.newtab.choice based
+ * on the value of browser.newtab.url
+ *
+ * @returns the value of browser.newtab.choice
+ */
+ getNewtabChoice: function() {
+ let newtabUrlPref = Services.prefs.getCharPref("browser.newtab.url");
+ let browserHomepageUrl = Services.prefs.getComplexValue("browser.startup.homepage",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ let newtabUrlSanitizedPref = browserHomepageUrl.split("|")[0];
+ let defaultStartupHomepage = Services.prefs.getDefaultBranch("browser.")
+ .getComplexValue("startup.homepage",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ switch (newtabUrlPref) {
+ case "about:logopage":
+ return 1;
+ case defaultStartupHomepage:
+ return 2;
+ case newtabUrlSanitizedPref:
+ return 3;
+ case "about:newtab":
+ return 4;
+ default: // Custom URL entered.
+ // We need this to consider instantApply.
+ this.newtabPageCustom = newtabUrlPref;
+ return 0;
+ }
+ }
+};
diff --git a/browser/components/preferences/permissions.js b/browser/components/preferences/permissions.js
new file mode 100644
index 000000000..a3c7c1b48
--- /dev/null
+++ b/browser/components/preferences/permissions.js
@@ -0,0 +1,459 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
+const nsICookiePermission = Components.interfaces.nsICookiePermission;
+
+const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
+
+function Permission(principal, type, capability)
+{
+ this.principal = principal;
+ this.origin = principal.origin;
+ this.type = type;
+ this.capability = capability;
+}
+
+var gPermissionManager = {
+ _type : "",
+ _permissions : [],
+ _permissionsToAdd : new Map(),
+ _permissionsToDelete : new Map(),
+ _bundle : null,
+ _tree : null,
+ _observerRemoved : false,
+
+ _view: {
+ _rowCount: 0,
+ get rowCount()
+ {
+ return this._rowCount;
+ },
+ getCellText: function (aRow, aColumn)
+ {
+ if (aColumn.id == "siteCol")
+ return gPermissionManager._permissions[aRow].origin;
+ else if (aColumn.id == "statusCol")
+ return gPermissionManager._permissions[aRow].capability;
+ return "";
+ },
+
+ isSeparator: function(aIndex) { return false; },
+ isSorted: function() { return false; },
+ isContainer: function(aIndex) { return false; },
+ setTree: function(aTree){},
+ getImageSrc: function(aRow, aColumn) {},
+ getProgressMode: function(aRow, aColumn) {},
+ getCellValue: function(aRow, aColumn) {},
+ cycleHeader: function(column) {},
+ getRowProperties: function(row){ return ""; },
+ getColumnProperties: function(column){ return ""; },
+ getCellProperties: function(row,column){
+ if (column.element.getAttribute("id") == "siteCol")
+ return "ltr";
+
+ return "";
+ }
+ },
+
+ _getCapabilityString: function (aCapability)
+ {
+ var stringKey = null;
+ switch (aCapability) {
+ case nsIPermissionManager.ALLOW_ACTION:
+ stringKey = "can";
+ break;
+ case nsIPermissionManager.DENY_ACTION:
+ stringKey = "cannot";
+ break;
+ case nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY:
+ stringKey = "canAccessFirstParty";
+ break;
+ case nsICookiePermission.ACCESS_SESSION:
+ stringKey = "canSession";
+ break;
+ }
+ return this._bundle.getString(stringKey);
+ },
+
+ addPermission: function (aCapability)
+ {
+ var textbox = document.getElementById("url");
+ var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
+ let principal;
+ try {
+ // The origin accessor on the principal object will throw if the
+ // principal doesn't have a canonical origin representation. This will
+ // help catch cases where the URI parser parsed something like
+ // `localhost:8080` as having the scheme `localhost`, rather than being
+ // an invalid URI. A canonical origin representation is required by the
+ // permission manager for storage, so this won't prevent any valid
+ // permissions from being entered by the user.
+ let uri;
+ try {
+ uri = Services.io.newURI(input_url, null, null);
+ principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+ // If we have ended up with an unknown scheme, the following will throw.
+ principal.origin;
+ } catch(ex) {
+ uri = Services.io.newURI("http://" + input_url, null, null);
+ principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+ // If we have ended up with an unknown scheme, the following will throw.
+ principal.origin;
+ }
+ } catch(ex) {
+ var message = this._bundle.getString("invalidURI");
+ var title = this._bundle.getString("invalidURITitle");
+ Services.prompt.alert(window, title, message);
+ return;
+ }
+
+ var capabilityString = this._getCapabilityString(aCapability);
+
+ // check whether the permission already exists, if not, add it
+ let permissionExists = false;
+ let capabilityExists = false;
+ for (var i = 0; i < this._permissions.length; ++i) {
+ if (this._permissions[i].principal.equals(principal)) {
+ permissionExists = true;
+ capabilityExists = this._permissions[i].capability == capabilityString;
+ if (!capabilityExists) {
+ this._permissions[i].capability = capabilityString;
+ }
+ break;
+ }
+ }
+
+
+ let permissionParams = {principal: principal, type: this._type, capability: aCapability};
+ if (!permissionExists) {
+ this._permissionsToAdd.set(principal.origin, permissionParams);
+ this._addPermission(permissionParams);
+ }
+ else if (!capabilityExists) {
+ this._permissionsToAdd.set(principal.origin, permissionParams);
+ this._handleCapabilityChange();
+ }
+
+ textbox.value = "";
+ textbox.focus();
+
+ // covers a case where the site exists already, so the buttons don't disable
+ this.onHostInput(textbox);
+
+ // enable "remove all" button as needed
+ document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
+ },
+
+ _removePermission: function(aPermission)
+ {
+ this._removePermissionFromList(aPermission.principal);
+
+ // If this permission was added during this session, let's remove
+ // it from the pending adds list to prevent calls to the
+ // permission manager.
+ let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin);
+
+ if (!isNewPermission) {
+ this._permissionsToDelete.set(aPermission.principal.origin, aPermission);
+ }
+
+ },
+
+ _handleCapabilityChange: function ()
+ {
+ // Re-do the sort, if the status changed from Block to Allow
+ // or vice versa, since if we're sorted on status, we may no
+ // longer be in order.
+ if (this._lastPermissionSortColumn == "statusCol") {
+ this._resortPermissions();
+ }
+ this._tree.treeBoxObject.invalidate();
+ },
+
+ _addPermission: function(aPermission)
+ {
+ this._addPermissionToList(aPermission);
+ ++this._view._rowCount;
+ this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
+ // Re-do the sort, since we inserted this new item at the end.
+ this._resortPermissions();
+ },
+
+ _resortPermissions: function()
+ {
+ gTreeUtils.sort(this._tree, this._view, this._permissions,
+ this._lastPermissionSortColumn,
+ this._permissionsComparator,
+ this._lastPermissionSortColumn,
+ !this._lastPermissionSortAscending); // keep sort direction
+ },
+
+ onHostInput: function (aSiteField)
+ {
+ document.getElementById("btnSession").disabled = !aSiteField.value;
+ document.getElementById("btnBlock").disabled = !aSiteField.value;
+ document.getElementById("btnAllow").disabled = !aSiteField.value;
+ },
+
+ onWindowKeyPress: function (aEvent)
+ {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
+ window.close();
+ },
+
+ onHostKeyPress: function (aEvent)
+ {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
+ document.getElementById("btnAllow").click();
+ },
+
+ onLoad: function ()
+ {
+ this._bundle = document.getElementById("bundlePreferences");
+ var params = window.arguments[0];
+ this.init(params);
+ },
+
+ init: function (aParams)
+ {
+ if (this._type) {
+ // reusing an open dialog, clear the old observer
+ this.uninit();
+ }
+
+ this._type = aParams.permissionType;
+ this._manageCapability = aParams.manageCapability;
+
+ var permissionsText = document.getElementById("permissionsText");
+ while (permissionsText.hasChildNodes())
+ permissionsText.removeChild(permissionsText.firstChild);
+ permissionsText.appendChild(document.createTextNode(aParams.introText));
+
+ document.title = aParams.windowTitle;
+
+ document.getElementById("btnBlock").hidden = !aParams.blockVisible;
+ document.getElementById("btnSession").hidden = !aParams.sessionVisible;
+ document.getElementById("btnAllow").hidden = !aParams.allowVisible;
+
+ var urlFieldVisible = (aParams.blockVisible || aParams.sessionVisible || aParams.allowVisible);
+
+ var urlField = document.getElementById("url");
+ urlField.value = aParams.prefilledHost;
+ urlField.hidden = !urlFieldVisible;
+
+ this.onHostInput(urlField);
+
+ var urlLabel = document.getElementById("urlLabel");
+ urlLabel.hidden = !urlFieldVisible;
+
+ let treecols = document.getElementsByTagName("treecols")[0];
+ treecols.addEventListener("click", event => {
+ if (event.target.nodeName != "treecol" || event.button != 0) {
+ return;
+ }
+
+ let sortField = event.target.getAttribute("data-field-name");
+ if (!sortField) {
+ return;
+ }
+
+ gPermissionManager.onPermissionSort(sortField);
+ });
+
+ Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
+ Services.obs.addObserver(this, "perm-changed", false);
+
+ this._loadPermissions();
+
+ urlField.focus();
+ },
+
+ uninit: function ()
+ {
+ if (!this._observerRemoved) {
+ Services.obs.removeObserver(this, "perm-changed");
+
+ this._observerRemoved = true;
+ }
+ },
+
+ observe: function (aSubject, aTopic, aData)
+ {
+ if (aTopic == "perm-changed") {
+ var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
+
+ // Ignore unrelated permission types.
+ if (permission.type != this._type)
+ return;
+
+ if (aData == "added") {
+ this._addPermission(permission);
+ }
+ else if (aData == "changed") {
+ for (var i = 0; i < this._permissions.length; ++i) {
+ if (permission.matches(this._permissions[i].principal, true)) {
+ this._permissions[i].capability = this._getCapabilityString(permission.capability);
+ break;
+ }
+ }
+ this._handleCapabilityChange();
+ }
+ else if (aData == "deleted") {
+ this._removePermissionFromList(permission.principal);
+ }
+ }
+ },
+
+ onPermissionSelected: function ()
+ {
+ var hasSelection = this._tree.view.selection.count > 0;
+ var hasRows = this._tree.view.rowCount > 0;
+ document.getElementById("removePermission").disabled = !hasRows || !hasSelection;
+ document.getElementById("removeAllPermissions").disabled = !hasRows;
+ },
+
+ onPermissionDeleted: function ()
+ {
+ if (!this._view.rowCount)
+ return;
+ var removedPermissions = [];
+ gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions);
+ for (var i = 0; i < removedPermissions.length; ++i) {
+ var p = removedPermissions[i];
+ this._removePermission(p);
+ }
+ document.getElementById("removePermission").disabled = !this._permissions.length;
+ document.getElementById("removeAllPermissions").disabled = !this._permissions.length;
+ },
+
+ onAllPermissionsDeleted: function ()
+ {
+ if (!this._view.rowCount)
+ return;
+ var removedPermissions = [];
+ gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions);
+ for (var i = 0; i < removedPermissions.length; ++i) {
+ var p = removedPermissions[i];
+ this._removePermission(p);
+ }
+ document.getElementById("removePermission").disabled = true;
+ document.getElementById("removeAllPermissions").disabled = true;
+ },
+
+ onPermissionKeyPress: function (aEvent)
+ {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
+ this.onPermissionDeleted();
+ }
+ },
+
+ _lastPermissionSortColumn: "",
+ _lastPermissionSortAscending: false,
+ _permissionsComparator : function (a, b)
+ {
+ return a.toLowerCase().localeCompare(b.toLowerCase());
+ },
+
+
+ onPermissionSort: function (aColumn)
+ {
+ this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
+ this._view,
+ this._permissions,
+ aColumn,
+ this._permissionsComparator,
+ this._lastPermissionSortColumn,
+ this._lastPermissionSortAscending);
+ this._lastPermissionSortColumn = aColumn;
+ },
+
+ onApplyChanges: function()
+ {
+ // Stop observing permission changes since we are about
+ // to write out the pending adds/deletes and don't need
+ // to update the UI
+ this.uninit();
+
+ for (let permissionParams of this._permissionsToAdd.values()) {
+ Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability);
+ }
+
+ for (let p of this._permissionsToDelete.values()) {
+ Services.perms.removeFromPrincipal(p.principal, p.type);
+ }
+
+ window.close();
+ },
+
+ _loadPermissions: function ()
+ {
+ this._tree = document.getElementById("permissionsTree");
+ this._permissions = [];
+
+ // load permissions into a table
+ var count = 0;
+ var enumerator = Services.perms.enumerator;
+ while (enumerator.hasMoreElements()) {
+ var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
+ this._addPermissionToList(nextPermission);
+ }
+
+ this._view._rowCount = this._permissions.length;
+
+ // sort and display the table
+ this._tree.view = this._view;
+ this.onPermissionSort("origin");
+
+ // disable "remove all" button if there are none
+ document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
+ },
+
+ _addPermissionToList: function (aPermission)
+ {
+ if (aPermission.type == this._type &&
+ (!this._manageCapability ||
+ (aPermission.capability == this._manageCapability))) {
+
+ var principal = aPermission.principal;
+ var capabilityString = this._getCapabilityString(aPermission.capability);
+ var p = new Permission(principal,
+ aPermission.type,
+ capabilityString);
+ this._permissions.push(p);
+ }
+ },
+
+ _removePermissionFromList: function (aPrincipal)
+ {
+ for (let i = 0; i < this._permissions.length; ++i) {
+ if (this._permissions[i].principal.equals(aPrincipal)) {
+ this._permissions.splice(i, 1);
+ this._view._rowCount--;
+ this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
+ this._tree.treeBoxObject.invalidate();
+ break;
+ }
+ }
+ },
+
+ setOrigin: function (aOrigin)
+ {
+ document.getElementById("url").value = aOrigin;
+ }
+};
+
+function setOrigin(aOrigin)
+{
+ gPermissionManager.setOrigin(aOrigin);
+}
+
+function initWithParams(aParams)
+{
+ gPermissionManager.init(aParams);
+}
+
diff --git a/browser/components/preferences/permissions.xul b/browser/components/preferences/permissions.xul
new file mode 100644
index 000000000..33806cc27
--- /dev/null
+++ b/browser/components/preferences/permissions.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: Java; tab-width: 4; 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >
+
+<window id="PermissionsDialog" class="windowDialog"
+ windowtype="Browser:Permissions"
+ title="&window.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: &window.width;;"
+ onload="gPermissionManager.onLoad();"
+ onunload="gPermissionManager.uninit();"
+ persist="screenX screenY width height"
+ onkeypress="gPermissionManager.onWindowKeyPress(event);">
+
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://browser/content/preferences/permissions.js"/>
+
+ <stringbundle id="bundlePreferences"
+ src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <keyset>
+ <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
+ </keyset>
+
+ <vbox class="contentPane" flex="1">
+ <description id="permissionsText" control="url"/>
+ <separator class="thin"/>
+ <label id="urlLabel" control="url" value="&address.label;" accesskey="&address.accesskey;"/>
+ <hbox align="start">
+ <textbox id="url" flex="1"
+ oninput="gPermissionManager.onHostInput(event.target);"
+ onkeypress="gPermissionManager.onHostKeyPress(event);"/>
+ </hbox>
+ <hbox pack="end">
+ <button id="btnBlock" disabled="true" label="&block.label;" accesskey="&block.accesskey;"
+ oncommand="gPermissionManager.addPermission(nsIPermissionManager.DENY_ACTION);"/>
+ <button id="btnSession" disabled="true" label="&session.label;" accesskey="&session.accesskey;"
+ oncommand="gPermissionManager.addPermission(nsICookiePermission.ACCESS_SESSION);"/>
+ <button id="btnAllow" disabled="true" label="&allow.label;" default="true" accesskey="&allow.accesskey;"
+ oncommand="gPermissionManager.addPermission(nsIPermissionManager.ALLOW_ACTION);"/>
+ </hbox>
+ <separator class="thin"/>
+ <tree id="permissionsTree" flex="1" style="height: 18em;"
+ hidecolumnpicker="true"
+ onkeypress="gPermissionManager.onPermissionKeyPress(event)"
+ onselect="gPermissionManager.onPermissionSelected();">
+ <treecols>
+ <treecol id="siteCol" label="&treehead.sitename.label;" flex="3"
+ data-field-name="origin" persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="statusCol" label="&treehead.status.label;" flex="1"
+ data-field-name="capability" persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </vbox>
+ <vbox>
+ <hbox class="actionButtons" align="left" flex="1">
+ <button id="removePermission" disabled="true"
+ accesskey="&removepermission.accesskey;"
+ icon="remove" label="&removepermission.label;"
+ oncommand="gPermissionManager.onPermissionDeleted();"/>
+ <button id="removeAllPermissions"
+ icon="clear" label="&removeallpermissions.label;"
+ accesskey="&removeallpermissions.accesskey;"
+ oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
+ </hbox>
+ <spacer flex="1"/>
+ <hbox class="actionButtons" align="right" flex="1">
+ <button oncommand="close();" icon="close"
+ label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
+ <button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
+ label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
+ </hbox>
+ <resizer type="window" dir="bottomend"/>
+ </vbox>
+</window>
diff --git a/browser/components/preferences/preferences.xul b/browser/components/preferences/preferences.xul
new file mode 100644
index 000000000..b56b16ecc
--- /dev/null
+++ b/browser/components/preferences/preferences.xul
@@ -0,0 +1,77 @@
+<?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/global.css"?>
+<?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
+
+<!-- XXX This should be in applications.xul, but bug 393953 means putting it
+ - there causes the Applications pane not to work the first time you open
+ - the Preferences dialog in a browsing session, so we work around the problem
+ - by putting it here instead.
+ -->
+<?xml-stylesheet href="chrome://browser/content/preferences/handlers.css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
+
+<!DOCTYPE prefwindow [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % preferencesDTD SYSTEM "chrome://browser/locale/preferences/preferences.dtd">
+%brandDTD;
+%preferencesDTD;
+]>
+
+#ifdef XP_WIN
+#define USE_WIN_TITLE_STYLE
+#endif
+
+<prefwindow type="prefwindow"
+ id="BrowserPreferences"
+ windowtype="Browser:Preferences"
+ ondialoghelp="openPrefsHelp()"
+#ifdef USE_WIN_TITLE_STYLE
+ title="&prefWindow.titleWin;"
+#else
+#ifdef XP_UNIX
+ title="&prefWindow.titleGNOME;"
+#endif
+#endif
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+#ifdef USE_WIN_TITLE_STYLE
+ style="&prefWinMinSize.styleWin2;"
+#else
+ style="&prefWinMinSize.styleGNOME;"
+#endif
+ onunload="if (typeof gSecurityPane != 'undefined') gSecurityPane.syncAddonSecurityLevel();"
+ ondialogaccept="gNewtabUrl.writeNewtabUrl();">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+ <script type="application/javascript" src="chrome://browser/content/preferences/newtaburl.js"/>
+
+ <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundlePreferences"
+ src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <prefpane id="paneMain" label="&paneGeneral.title;"
+ src="chrome://browser/content/preferences/main.xul"/>
+ <prefpane id="paneTabs" label="&paneTabs.title;"
+ src="chrome://browser/content/preferences/tabs.xul"/>
+ <prefpane id="paneContent" label="&paneContent.title;"
+ src="chrome://browser/content/preferences/content.xul"/>
+ <prefpane id="paneApplications" label="&paneApplications.title;"
+ src="chrome://browser/content/preferences/applications.xul"/>
+ <prefpane id="panePrivacy" label="&panePrivacy.title;"
+ src="chrome://browser/content/preferences/privacy.xul"/>
+ <prefpane id="paneSecurity" label="&paneSecurity.title;"
+ src="chrome://browser/content/preferences/security.xul"/>
+#ifdef MOZ_SERVICES_SYNC
+ <prefpane id="paneSync" label="&paneSync.title;"
+ src="chrome://browser/content/preferences/sync.xul"/>
+#endif
+ <prefpane id="paneAdvanced" label="&paneAdvanced.title;"
+ src="chrome://browser/content/preferences/advanced.xul"/>
+
+</prefwindow>
+
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
new file mode 100644
index 000000000..05ed3bcdd
--- /dev/null
+++ b/browser/components/preferences/privacy.js
@@ -0,0 +1,458 @@
+// 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/XPCOMUtils.jsm");
+
+var gPrivacyPane = {
+
+ /**
+ * Whether the use has selected the auto-start private browsing mode in the UI.
+ */
+ _autoStartPrivateBrowsing: false,
+
+ /**
+ * Whether the prompt to restart Firefox should appear when changing the autostart pref.
+ */
+ _shouldPromptForRestart: true,
+
+ /**
+ * Sets up the UI for the number of days of history to keep, and updates the
+ * label of the "Clear Now..." button.
+ */
+ init: function()
+ {
+ this._updateSanitizeSettingsButton();
+ this.initializeHistoryMode();
+ this.updateHistoryModePane();
+ this.updatePrivacyMicroControls();
+ this.initAutoStartPrivateBrowsingReverter();
+ },
+
+ // HISTORY MODE
+
+ /**
+ * The list of preferences which affect the initial history mode settings.
+ * If the auto start private browsing mode pref is active, the initial
+ * history mode would be set to "Don't remember anything".
+ * If all of these preferences have their default values, and the auto-start
+ * private browsing mode is not active, the initial history mode would be
+ * set to "Remember everything".
+ * Otherwise, the initial history mode would be set to "Custom".
+ *
+ * Extensions adding their own preferences can append their IDs to this array if needed.
+ */
+ prefsForDefault: [
+ "places.history.enabled",
+ "browser.formfill.enable",
+ "network.cookie.cookieBehavior",
+ "network.cookie.lifetimePolicy",
+ "privacy.sanitize.sanitizeOnShutdown"
+ ],
+
+ /**
+ * The list of control IDs which are dependent on the auto-start private
+ * browsing setting, such that in "Custom" mode they would be disabled if
+ * the auto-start private browsing checkbox is checked, and enabled otherwise.
+ *
+ * Extensions adding their own controls can append their IDs to this array if needed.
+ */
+ dependentControls: [
+ "rememberHistory",
+ "rememberForms",
+ "keepUntil",
+ "keepCookiesUntil",
+ "alwaysClear",
+ "clearDataSettings"
+ ],
+
+ /**
+ * Check whether all the preferences values are set to their default values
+ *
+ * @param aPrefs an array of pref names to check for
+ * @returns boolean true if all of the prefs are set to their default values,
+ * false otherwise
+ */
+ _checkDefaultValues: function(aPrefs) {
+ for (let i = 0; i < aPrefs.length; ++i) {
+ let pref = document.getElementById(aPrefs[i]);
+ if (pref.value != pref.defaultValue)
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Initialize the history mode menulist based on the privacy preferences
+ */
+ initializeHistoryMode: function()
+ {
+ let mode;
+ let getVal = function(aPref)
+ document.getElementById(aPref).value;
+
+ if (this._checkDefaultValues(this.prefsForDefault)) {
+ if (getVal("browser.privatebrowsing.autostart"))
+ mode = "dontremember";
+ else
+ mode = "remember";
+ }
+ else
+ mode = "custom";
+
+ document.getElementById("historyMode").value = mode;
+ },
+
+ /**
+ * Update the selected pane based on the history mode menulist
+ */
+ updateHistoryModePane: function()
+ {
+ let selectedIndex = -1;
+ switch (document.getElementById("historyMode").value) {
+ case "remember":
+ selectedIndex = 0;
+ break;
+ case "dontremember":
+ selectedIndex = 1;
+ break;
+ case "custom":
+ selectedIndex = 2;
+ break;
+ }
+ document.getElementById("historyPane").selectedIndex = selectedIndex;
+ },
+
+ /**
+ * Update the private browsing auto-start pref and the history mode
+ * micro-management prefs based on the history mode menulist
+ */
+ updateHistoryModePrefs: function()
+ {
+ let pref = document.getElementById("browser.privatebrowsing.autostart");
+ switch (document.getElementById("historyMode").value) {
+ case "remember":
+ if (pref.value)
+ pref.value = false;
+
+ // select the remember history option
+ document.getElementById("places.history.enabled").value = true;
+
+ // select the remember forms history option
+ document.getElementById("browser.formfill.enable").value = true;
+
+ // select the accept cookies option
+ document.getElementById("network.cookie.cookieBehavior").value = 0;
+ // select the cookie lifetime policy option
+ document.getElementById("network.cookie.lifetimePolicy").value = 0;
+
+ // select the clear on close option
+ document.getElementById("privacy.sanitize.sanitizeOnShutdown").value = false;
+ break;
+ case "dontremember":
+ if (!pref.value)
+ pref.value = true;
+ break;
+ }
+ },
+
+ /**
+ * Update the privacy micro-management controls based on the
+ * value of the private browsing auto-start checkbox.
+ */
+ updatePrivacyMicroControls: function()
+ {
+ if (document.getElementById("historyMode").value == "custom") {
+ let disabled = this._autoStartPrivateBrowsing =
+ document.getElementById("privateBrowsingAutoStart").checked;
+ this.dependentControls
+ .forEach(function(aElement)
+ document.getElementById(aElement).disabled = disabled);
+
+ const Ci = Components.interfaces;
+ // adjust the cookie controls status
+ this.readAcceptCookies();
+ let lifetimePolicy = document.getElementById("network.cookie.lifetimePolicy").value;
+ if (lifetimePolicy != Ci.nsICookieService.ACCEPT_NORMALLY &&
+ lifetimePolicy != Ci.nsICookieService.ACCEPT_SESSION &&
+ lifetimePolicy != Ci.nsICookieService.ACCEPT_FOR_N_DAYS) {
+ lifetimePolicy = Ci.nsICookieService.ACCEPT_NORMALLY;
+ }
+ document.getElementById("keepCookiesUntil").value = disabled ? 2 : lifetimePolicy;
+
+ // adjust the checked state of the sanitizeOnShutdown checkbox
+ document.getElementById("alwaysClear").checked = disabled ? false :
+ document.getElementById("privacy.sanitize.sanitizeOnShutdown").value;
+
+ // adjust the checked state of the remember history checkboxes
+ document.getElementById("rememberHistory").checked = disabled ? false :
+ document.getElementById("places.history.enabled").value;
+ document.getElementById("rememberForms").checked = disabled ? false :
+ document.getElementById("browser.formfill.enable").value;
+
+ if (!disabled) {
+ // adjust the Settings button for sanitizeOnShutdown
+ this._updateSanitizeSettingsButton();
+ }
+ }
+ },
+
+ // PRIVATE BROWSING
+
+ /**
+ * Initialize the starting state for the auto-start private browsing mode pref reverter.
+ */
+ initAutoStartPrivateBrowsingReverter: function()
+ {
+ let mode = document.getElementById("historyMode");
+ let autoStart = document.getElementById("privateBrowsingAutoStart");
+ this._lastMode = mode.selectedIndex;
+ this._lastCheckState = autoStart.hasAttribute('checked');
+ },
+
+ _lastMode: null,
+ _lasCheckState: null,
+ updateAutostart: function() {
+ let mode = document.getElementById("historyMode");
+ let autoStart = document.getElementById("privateBrowsingAutoStart");
+ let pref = document.getElementById("browser.privatebrowsing.autostart");
+ if ((mode.value == "custom" && this._lastCheckState == autoStart.checked) ||
+ (mode.value == "remember" && !this._lastCheckState) ||
+ (mode.value == "dontremember" && this._lastCheckState)) {
+ // These are all no-op changes, so we don't need to prompt.
+ this._lastMode = mode.selectedIndex;
+ this._lastCheckState = autoStart.hasAttribute('checked');
+ return;
+ }
+
+ if (!this._shouldPromptForRestart) {
+ // We're performing a revert. Just let it happen.
+ return;
+ }
+
+ const Cc = Components.classes, Ci = Components.interfaces;
+ let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+ let bundle = document.getElementById("bundlePreferences");
+ let msg = bundle.getFormattedString(autoStart.checked ?
+ "featureEnableRequiresRestart" : "featureDisableRequiresRestart",
+ [brandName]);
+ let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
+ let prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
+ let shouldProceed = prompts.confirm(window, title, msg)
+ if (shouldProceed) {
+ let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+ "restart");
+ shouldProceed = !cancelQuit.data;
+
+ if (shouldProceed) {
+ pref.value = autoStart.hasAttribute('checked');
+ document.documentElement.acceptDialog();
+ let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
+ .getService(Ci.nsIAppStartup);
+ appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+ return;
+ }
+ }
+
+ this._shouldPromptForRestart = false;
+
+ if (this._lastCheckState) {
+ autoStart.checked = "checked";
+ } else {
+ autoStart.removeAttribute('checked');
+ }
+ mode.selectedIndex = this._lastMode;
+ mode.doCommand();
+
+ this._shouldPromptForRestart = true;
+ },
+
+ // HISTORY
+
+ /*
+ * Preferences:
+ *
+ * places.history.enabled
+ * - whether history is enabled or not
+ * browser.formfill.enable
+ * - true if entries in forms and the search bar should be saved, false
+ * otherwise
+ */
+
+ // COOKIES
+
+ /*
+ * Preferences:
+ *
+ * network.cookie.cookieBehavior
+ * - determines how the browser should handle cookies:
+ * 0 means enable all cookies
+ * 1 means reject all third party cookies
+ * 2 means disable all cookies
+ * 3 means reject third party cookies unless at least one is already set for the eTLD
+ * see netwerk/cookie/src/nsCookieService.cpp for details
+ * network.cookie.lifetimePolicy
+ * - determines how long cookies are stored:
+ * 0 means keep cookies until they expire
+ * 2 means keep cookies until the browser is closed
+ */
+
+ /**
+ * Reads the network.cookie.cookieBehavior preference value and
+ * enables/disables the rest of the cookie UI accordingly, returning true
+ * if cookies are enabled.
+ */
+ readAcceptCookies: function()
+ {
+ var pref = document.getElementById("network.cookie.cookieBehavior");
+ var acceptThirdPartyLabel = document.getElementById("acceptThirdPartyLabel");
+ var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");
+ var keepUntil = document.getElementById("keepUntil");
+ var menu = document.getElementById("keepCookiesUntil");
+
+ // enable the rest of the UI for anything other than "disable all cookies"
+ var acceptCookies = (pref.value != 2);
+
+ acceptThirdPartyLabel.disabled = acceptThirdPartyMenu.disabled = !acceptCookies;
+ keepUntil.disabled = menu.disabled = this._autoStartPrivateBrowsing || !acceptCookies;
+
+ return acceptCookies;
+ },
+
+ /**
+ * Enables/disables the "keep until" label and menulist in response to the
+ * "accept cookies" checkbox being checked or unchecked.
+ */
+ writeAcceptCookies: function()
+ {
+ var accept = document.getElementById("acceptCookies");
+ var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");
+
+ // if we're enabling cookies, automatically select 'accept third party always'
+ if (accept.checked)
+ acceptThirdPartyMenu.selectedIndex = 0;
+
+ return accept.checked ? 0 : 2;
+ },
+
+ /**
+ * Converts between network.cookie.cookieBehavior and the third-party cookie UI
+ */
+ readAcceptThirdPartyCookies: function()
+ {
+ var pref = document.getElementById("network.cookie.cookieBehavior");
+ switch (pref.value)
+ {
+ case 0:
+ return "always";
+ case 1:
+ return "never";
+ case 2:
+ return "never";
+ case 3:
+ return "visited";
+ default:
+ return undefined;
+ }
+ },
+
+ writeAcceptThirdPartyCookies: function()
+ {
+ var accept = document.getElementById("acceptThirdPartyMenu").selectedItem;
+ switch (accept.value)
+ {
+ case "always":
+ return 0;
+ case "visited":
+ return 3;
+ case "never":
+ return 1;
+ default:
+ return undefined;
+ }
+ },
+
+ /**
+ * Displays fine-grained, per-site preferences for cookies.
+ */
+ showCookieExceptions: function()
+ {
+ var bundlePreferences = document.getElementById("bundlePreferences");
+ var params = { blockVisible : true,
+ sessionVisible : true,
+ allowVisible : true,
+ prefilledHost : "",
+ permissionType : "cookie",
+ windowTitle : bundlePreferences.getString("cookiepermissionstitle"),
+ introText : bundlePreferences.getString("cookiepermissionstext") };
+ document.documentElement.openWindow("Browser:Permissions",
+ "chrome://browser/content/preferences/permissions.xul",
+ "", params);
+ },
+
+ /**
+ * Displays all the user's cookies in a dialog.
+ */
+ showCookies: function(aCategory)
+ {
+ document.documentElement.openWindow("Browser:Cookies",
+ "chrome://browser/content/preferences/cookies.xul",
+ "", null);
+ },
+
+ // CLEAR PRIVATE DATA
+
+ /*
+ * Preferences:
+ *
+ * privacy.sanitize.sanitizeOnShutdown
+ * - true if the user's private data is cleared on startup according to the
+ * Clear Private Data settings, false otherwise
+ */
+
+ /**
+ * Displays the Clear Private Data settings dialog.
+ */
+ showClearPrivateDataSettings: function()
+ {
+ document.documentElement.openSubDialog("chrome://browser/content/preferences/sanitize.xul",
+ "", null);
+ },
+
+
+ /**
+ * Displays a dialog from which individual parts of private data may be
+ * cleared.
+ */
+ clearPrivateDataNow: function(aClearEverything)
+ {
+ var ts = document.getElementById("privacy.sanitize.timeSpan");
+ var timeSpanOrig = ts.value;
+ if (aClearEverything)
+ ts.value = 0;
+
+ const Cc = Components.classes, Ci = Components.interfaces;
+ var glue = Cc["@mozilla.org/browser/browserglue;1"]
+ .getService(Ci.nsIBrowserGlue);
+ glue.sanitize(window);
+
+ // reset the timeSpan pref
+ if (aClearEverything)
+ ts.value = timeSpanOrig;
+ Services.obs.notifyObservers(null, "clear-private-data", null);
+ },
+
+ /**
+ * Enables or disables the "Settings..." button depending
+ * on the privacy.sanitize.sanitizeOnShutdown preference value
+ */
+ _updateSanitizeSettingsButton: function() {
+ var settingsButton = document.getElementById("clearDataSettings");
+ var sanitizeOnShutdownPref = document.getElementById("privacy.sanitize.sanitizeOnShutdown");
+
+ settingsButton.disabled = !sanitizeOnShutdownPref.value;
+ }
+
+};
diff --git a/browser/components/preferences/privacy.xul b/browser/components/preferences/privacy.xul
new file mode 100644
index 000000000..e5175422d
--- /dev/null
+++ b/browser/components/preferences/privacy.xul
@@ -0,0 +1,256 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: XML; tab-width: 4; 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/. -->
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
+%brandDTD;
+%privacyDTD;
+]>
+
+<overlay id="PrivacyPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <prefpane id="panePrivacy"
+ onpaneload="gPrivacyPane.init();"
+ helpTopic="prefs-privacy">
+
+ <preferences id="privacyPreferences">
+
+ <!-- Global Privacy Control -->
+ <preference id="privacy.GPCheader.enabled"
+ name="privacy.GPCheader.enabled"
+ type="bool"/>
+
+ <!-- XXX button prefs -->
+ <preference id="pref.privacy.disable_button.cookie_exceptions"
+ name="pref.privacy.disable_button.cookie_exceptions"
+ type="bool"/>
+ <preference id="pref.privacy.disable_button.view_cookies"
+ name="pref.privacy.disable_button.view_cookies"
+ type="bool"/>
+
+ <!-- Location Bar -->
+ <preference id="browser.urlbar.autocomplete.enabled"
+ name="browser.urlbar.autocomplete.enabled"
+ type="bool"/>
+ <preference id="browser.urlbar.suggest.bookmark"
+ name="browser.urlbar.suggest.bookmark"
+ type="bool"/>
+ <preference id="browser.urlbar.suggest.history"
+ name="browser.urlbar.suggest.history"
+ type="bool"/>
+ <preference id="browser.urlbar.suggest.openpage"
+ name="browser.urlbar.suggest.openpage"
+ type="bool"/>
+
+ <!-- History -->
+ <preference id="places.history.enabled"
+ name="places.history.enabled"
+ type="bool"/>
+ <preference id="browser.formfill.enable"
+ name="browser.formfill.enable"
+ type="bool"/>
+
+ <!-- Cookies -->
+ <preference id="network.cookie.cookieBehavior" name="network.cookie.cookieBehavior" type="int"/>
+ <preference id="network.cookie.lifetimePolicy" name="network.cookie.lifetimePolicy" type="int"/>
+ <preference id="network.cookie.blockFutureCookies" name="network.cookie.blockFutureCookies" type="bool"/>
+
+ <!-- Clear Private Data -->
+ <preference id="privacy.sanitize.sanitizeOnShutdown"
+ name="privacy.sanitize.sanitizeOnShutdown"
+ onchange="gPrivacyPane._updateSanitizeSettingsButton();"
+ type="bool"/>
+ <preference id="privacy.sanitize.timeSpan"
+ name="privacy.sanitize.timeSpan"
+ type="int"/>
+
+ <!-- Private Browsing -->
+ <preference id="browser.privatebrowsing.autostart"
+ name="browser.privatebrowsing.autostart"
+ onchange="gPrivacyPane.updatePrivacyMicroControls();"
+ type="bool"/>
+
+ </preferences>
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/privacy.js"/>
+
+ <!-- History -->
+ <groupbox id="historyPanel">
+ <caption>&history.label;</caption>
+ <hbox align="center">
+ <label id="historyModeLabel"
+ control="historyMode"
+ accesskey="&historyHeader.pre.accesskey;">&historyHeader.pre.label;</label>
+ <menulist id="historyMode"
+ oncommand="gPrivacyPane.updateHistoryModePane();
+ gPrivacyPane.updateHistoryModePrefs();
+ gPrivacyPane.updatePrivacyMicroControls();
+ gPrivacyPane.updateAutostart();">
+ <menupopup>
+ <menuitem label="&historyHeader.remember.label;" value="remember"/>
+ <menuitem label="&historyHeader.dontremember.label;" value="dontremember"/>
+ <menuitem label="&historyHeader.custom.label;" value="custom"/>
+ </menupopup>
+ </menulist>
+ <label>&historyHeader.post.label;</label>
+ </hbox>
+
+ <deck id="historyPane">
+ <vbox align="center" id="historyRememberPane">
+ <hbox align="center" flex="1">
+ <spacer flex="1" class="indent"/>
+ <vbox flex="2">
+ <description>&rememberDescription.label;</description>
+ <separator/>
+ <description>&rememberActions.pre.label;<html:a
+ class="inline-link" href="#"
+ onclick="gPrivacyPane.clearPrivateDataNow(false); return false;"
+ >&rememberActions.clearHistory.label;</html:a>&rememberActions.middle.label;<html:a
+ class="inline-link" href="#"
+ onclick="gPrivacyPane.showCookies(); return false;"
+ >&rememberActions.removeCookies.label;</html:a>&rememberActions.post.label;</description>
+ </vbox>
+ <spacer flex="1" class="indent"/>
+ </hbox>
+ </vbox>
+ <vbox align="center" id="historyDontRememberPane">
+ <hbox align="center" flex="1">
+ <spacer flex="1" class="indent"/>
+ <vbox flex="2">
+ <description>&dontrememberDescription.label;</description>
+ <separator/>
+ <description>&dontrememberActions.pre.label;<html:a
+ class="inline-link" href="#"
+ onclick="gPrivacyPane.clearPrivateDataNow(true); return false;"
+ >&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description>
+ </vbox>
+ <spacer flex="1" class="indent"/>
+ </hbox>
+ </vbox>
+ <vbox id="historyCustomPane">
+ <separator class="thin"/>
+ <checkbox id="privateBrowsingAutoStart" class="indent"
+ label="&privateBrowsingPermanent2.label;"
+ accesskey="&privateBrowsingPermanent2.accesskey;"
+ preference="browser.privatebrowsing.autostart"
+ oncommand="gPrivacyPane.updateAutostart()"/>
+
+ <vbox class="indent">
+ <vbox class="indent">
+ <checkbox id="rememberHistory"
+ label="&rememberHistory2.label;"
+ accesskey="&rememberHistory2.accesskey;"
+ preference="places.history.enabled"/>
+ <checkbox id="rememberForms"
+ label="&rememberSearchForm.label;"
+ accesskey="&rememberSearchForm.accesskey;"
+ preference="browser.formfill.enable"/>
+ <hbox id="cookiesBox">
+ <checkbox id="acceptCookies" label="&acceptCookies.label;" flex="1"
+ preference="network.cookie.cookieBehavior"
+ accesskey="&acceptCookies.accesskey;"
+ onsyncfrompreference="return gPrivacyPane.readAcceptCookies();"
+ onsynctopreference="return gPrivacyPane.writeAcceptCookies();"/>
+ <button id="cookieExceptions" oncommand="gPrivacyPane.showCookieExceptions();"
+ label="&cookieExceptions.label;" accesskey="&cookieExceptions.accesskey;"
+ preference="pref.privacy.disable_button.cookie_exceptions"/>
+ </hbox>
+ <hbox id="acceptThirdPartyRow" class="indent">
+ <hbox id="acceptThirdPartyBox" align="center">
+ <label id="acceptThirdPartyLabel" control="acceptThirdPartyMenu"
+ accesskey="&acceptThirdParty.pre.accesskey;">&acceptThirdParty.pre.label;</label>
+ <menulist id="acceptThirdPartyMenu" preference="network.cookie.cookieBehavior"
+ onsyncfrompreference="return gPrivacyPane.readAcceptThirdPartyCookies();"
+ onsynctopreference="return gPrivacyPane.writeAcceptThirdPartyCookies();">
+ <menupopup>
+ <menuitem label="&acceptThirdParty.always.label;" value="always"/>
+ <menuitem label="&acceptThirdParty.visited.label;" value="visited"/>
+ <menuitem label="&acceptThirdParty.never.label;" value="never"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+
+ <hbox id="keepRow" class="indent">
+ <hbox id="keepBox" align="center">
+ <label id="keepUntil"
+ control="keepCookiesUntil"
+ accesskey="&keepUntil.accesskey;">&keepUntil.label;</label>
+ <menulist id="keepCookiesUntil"
+ preference="network.cookie.lifetimePolicy">
+ <menupopup>
+ <menuitem label="&expire.label;" value="0"/>
+ <menuitem label="&close.label;" value="2"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <hbox flex="1"/>
+ <button id="showCookiesButton"
+ label="&showCookies.label;" accesskey="&showCookies.accesskey;"
+ oncommand="gPrivacyPane.showCookies();"
+ preference="pref.privacy.disable_button.view_cookies"/>
+ </hbox>
+
+ <hbox id="clearDataBox" align="center">
+ <checkbox id="alwaysClear" flex="1"
+ preference="privacy.sanitize.sanitizeOnShutdown"
+ label="&clearOnClose.label;"
+ accesskey="&clearOnClose.accesskey;"/>
+ <button id="clearDataSettings" label="&clearOnCloseSettings.label;"
+ accesskey="&clearOnCloseSettings.accesskey;"
+ oncommand="gPrivacyPane.showClearPrivateDataSettings();"/>
+ </hbox>
+ </vbox>
+ </vbox>
+ </vbox>
+ </deck>
+
+ </groupbox>
+
+ <!-- Global Privacy Control -->
+ <groupbox id="dataPrivacyPanel">
+ <caption>&dataPrivacy.label;</caption>
+ <hbox align="center">
+ <checkbox id="privacyGPCCheckbox"
+ label="&sendGPCheader.label;"
+ accesskey="&sendGPCheader.accesskey;"
+ preference="privacy.GPCheader.enabled"/>
+ <separator class="thin"/>
+ <label class="text-link" id="GPCInfo"
+ href="https://www.palemoon.org/support/global-privacy-control"
+ value="&GPCInfo.label;"/>
+
+ </hbox>
+ </groupbox>
+
+ <!-- Location Bar -->
+ <groupbox id="locatioBarPanel">
+ <caption>&locationBar.label;</caption>
+
+ <label id="locationBarSuggestionLabel">&locbar.suggest.label;</label>
+ <hbox id="tabPrefsBox" align="center" flex="1">
+ <checkbox id="historySuggestion" label="&locbar.history.label;"
+ accesskey="&locbar.history.accesskey;"
+ preference="browser.urlbar.suggest.history"/>
+ <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
+ accesskey="&locbar.bookmarks.accesskey;"
+ preference="browser.urlbar.suggest.bookmark"/>
+ <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
+ accesskey="&locbar.openpage.accesskey;"
+ preference="browser.urlbar.suggest.openpage"/>
+ </hbox>
+
+ </groupbox>
+
+ </prefpane>
+
+</overlay>
diff --git a/browser/components/preferences/sanitize.js b/browser/components/preferences/sanitize.js
new file mode 100644
index 000000000..4383bee4f
--- /dev/null
+++ b/browser/components/preferences/sanitize.js
@@ -0,0 +1,11 @@
+// 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 gSanitizeDialog = Object.freeze({
+ onClearHistoryChanged: function () {
+ let downloadsPref = document.getElementById("privacy.clearOnShutdown.downloads");
+ let historyPref = document.getElementById("privacy.clearOnShutdown.history");
+ downloadsPref.value = historyPref.value;
+ }
+});
diff --git a/browser/components/preferences/sanitize.xul b/browser/components/preferences/sanitize.xul
new file mode 100644
index 000000000..829b5dfc8
--- /dev/null
+++ b/browser/components/preferences/sanitize.xul
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: Java; tab-width: 4; 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
+ %brandDTD;
+ %sanitizeDTD;
+]>
+
+<prefwindow id="SanitizeDialog" type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ dlgbuttons="accept,cancel,help"
+ ondialoghelp="openPrefsHelp()"
+ style="width: &dialog.width2;;"
+ title="&sanitizePrefs2.title;"
+ onload="gSanitizeDialog.onClearHistoryChanged();">
+
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+ <script type="application/javascript" src="chrome://browser/content/preferences/sanitize.js"/>
+
+ <prefpane id="SanitizeDialogPane"
+ helpTopic="prefs-clear-private-data">
+
+ <preferences>
+ <preference id="privacy.clearOnShutdown.history" name="privacy.clearOnShutdown.history" type="bool"
+ onchange="return gSanitizeDialog.onClearHistoryChanged();"/>
+ <preference id="privacy.clearOnShutdown.formdata" name="privacy.clearOnShutdown.formdata" type="bool"/>
+ <preference id="privacy.clearOnShutdown.passwords" name="privacy.clearOnShutdown.passwords" type="bool"/>
+ <preference id="privacy.clearOnShutdown.downloads" name="privacy.clearOnShutdown.downloads" type="bool"/>
+ <preference id="privacy.clearOnShutdown.cookies" name="privacy.clearOnShutdown.cookies" type="bool"/>
+ <preference id="privacy.clearOnShutdown.cache" name="privacy.clearOnShutdown.cache" type="bool"/>
+ <preference id="privacy.clearOnShutdown.offlineApps" name="privacy.clearOnShutdown.offlineApps" type="bool"/>
+ <preference id="privacy.clearOnShutdown.sessions" name="privacy.clearOnShutdown.sessions" type="bool"/>
+ <preference id="privacy.clearOnShutdown.siteSettings" name="privacy.clearOnShutdown.siteSettings" type="bool"/>
+ <preference id="privacy.clearOnShutdown.connectivityData" name="privacy.clearOnShutdown.connectivityData" type="bool"/>
+ </preferences>
+
+ <description>&clearDataSettings2.label;</description>
+
+ <groupbox orient="horizontal">
+ <caption label="&historySection.label;"/>
+ <grid flex="1">
+ <columns>
+ <column style="width: &column.width2;"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <checkbox label="&itemHistoryAndDownloads.label;"
+ accesskey="&itemHistoryAndDownloads.accesskey;"
+ preference="privacy.clearOnShutdown.history"/>
+ <checkbox label="&itemCookies.label;"
+ accesskey="&itemCookies.accesskey;"
+ preference="privacy.clearOnShutdown.cookies"/>
+ </row>
+ <row>
+ <checkbox label="&itemActiveLogins.label;"
+ accesskey="&itemActiveLogins.accesskey;"
+ preference="privacy.clearOnShutdown.sessions"/>
+ <checkbox label="&itemCache.label;"
+ accesskey="&itemCache.accesskey;"
+ preference="privacy.clearOnShutdown.cache"/>
+ </row>
+ <row>
+ <checkbox label="&itemFormSearchHistory.label;"
+ accesskey="&itemFormSearchHistory.accesskey;"
+ preference="privacy.clearOnShutdown.formdata"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ <groupbox orient="horizontal">
+ <caption label="&dataSection.label;"/>
+ <grid flex="1">
+ <columns>
+ <column style="width: &column.width2;"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <checkbox label="&itemPasswords.label;"
+ accesskey="&itemPasswords.accesskey;"
+ preference="privacy.clearOnShutdown.passwords"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineApps.accesskey;"
+ preference="privacy.clearOnShutdown.offlineApps"/>
+ </row>
+ <row>
+ <checkbox label="&itemSitePreferences.label;"
+ accesskey="&itemSitePreferences.accesskey;"
+ preference="privacy.clearOnShutdown.siteSettings"/>
+ <checkbox label="&itemConnectivityData.label;"
+ accesskey="&itemConnectivityData.accesskey;"
+ preference="privacy.clearOnShutdown.connectivityData"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ </prefpane>
+</prefwindow>
diff --git a/browser/components/preferences/security.js b/browser/components/preferences/security.js
new file mode 100644
index 000000000..d8f491b1c
--- /dev/null
+++ b/browser/components/preferences/security.js
@@ -0,0 +1,235 @@
+// 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/.
+
+XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
+ "resource://gre/modules/LoginHelper.jsm");
+
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+var gSecurityPane = {
+ _pane: null,
+
+ /**
+ * Initializes UI.
+ */
+ init: function ()
+ {
+ this._pane = document.getElementById("paneSecurity");
+ this._initMasterPasswordUI();
+ },
+
+ // ADD-ONS
+
+ /*
+ * Preferences:
+ *
+ * xpinstall.whitelist.required
+ * - true if a site must be added to a site whitelist before extensions
+ * provided by the site may be installed from it, false if the extension
+ * may be directly installed after a confirmation dialog
+ */
+
+ /**
+ * Enables/disables the add-ons Exceptions button depending on whether
+ * or not add-on installation warnings are displayed.
+ */
+ readWarnAddonInstall: function ()
+ {
+ var warn = document.getElementById("xpinstall.whitelist.required");
+ var exceptions = document.getElementById("addonExceptions");
+
+ exceptions.disabled = !warn.value;
+
+ // don't override the preference value
+ return undefined;
+ },
+
+ /**
+ * Displays the exceptions lists for add-on installation warnings.
+ */
+ showAddonExceptions: function ()
+ {
+ var bundlePrefs = document.getElementById("bundlePreferences");
+
+ var params = this._addonParams;
+ if (!params.windowTitle || !params.introText) {
+ params.windowTitle = bundlePrefs.getString("addons_permissions_title");
+ params.introText = bundlePrefs.getString("addonspermissionstext");
+ }
+
+ document.documentElement.openWindow("Browser:Permissions",
+ "chrome://browser/content/preferences/permissions.xul",
+ "", params);
+ },
+
+ /**
+ * Parameters for the add-on install permissions dialog.
+ */
+ _addonParams:
+ {
+ blockVisible: false,
+ sessionVisible: false,
+ allowVisible: true,
+ prefilledHost: "",
+ permissionType: "install"
+ },
+
+ /**
+ * Ensures that the blocklist is enabled/disabled appropriately based on level
+ */
+ addonLevelNeedsSync: function()
+ {
+ Services.prefs.setBoolPref("extensions.blocklist.level.updated", true);
+ },
+ // called from preferences window onunload.
+ syncAddonSecurityLevel: function()
+ {
+ if (Services.prefs.getBoolPref("extensions.blocklist.level.updated") == true) {
+ Services.prefs.setBoolPref("extensions.blocklist.level.updated", false);
+ var secLevel = Services.prefs.getIntPref("extensions.blocklist.level");
+ Services.prefs.setBoolPref("extensions.blocklist.enabled",
+ !(secLevel == 99));
+ }
+ },
+
+ // PASSWORDS
+
+ /*
+ * Preferences:
+ *
+ * signon.rememberSignons
+ * - true if passwords are remembered, false otherwise
+ */
+
+ /**
+ * Enables/disables the Exceptions button used to configure sites where
+ * passwords are never saved. When browser is set to start in Private
+ * Browsing mode, the "Remember passwords" UI is useless, so we disable it.
+ */
+ readSavePasswords: function ()
+ {
+ var pref = document.getElementById("signon.rememberSignons");
+ var excepts = document.getElementById("passwordExceptions");
+
+ if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
+ document.getElementById("savePasswords").disabled = true;
+ excepts.disabled = true;
+ return false;
+ } else {
+ excepts.disabled = !pref.value;
+ // don't override pref value in UI
+ return undefined;
+ }
+ },
+
+ /**
+ * Displays a dialog in which the user can view and modify the list of sites
+ * where passwords are never saved.
+ */
+ showPasswordExceptions: function ()
+ {
+ let bundlePrefs = document.getElementById("bundlePreferences");
+ let params = {
+ blockVisible: true,
+ sessionVisible: false,
+ allowVisible: false,
+ hideStatusColumn: true,
+ prefilledHost: "",
+ permissionType: "login-saving",
+ windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"),
+ introText: bundlePrefs.getString("savedLoginsExceptions_desc")
+ };
+
+ document.documentElement.openWindow("Toolkit:PasswordManagerExceptions",
+ "chrome://browser/content/preferences/permissions.xul",
+ null, params);
+ },
+
+ /**
+ * Initializes master password UI: the "use master password" checkbox, selects
+ * the master password button to show, and enables/disables it as necessary.
+ * The master password is controlled by various bits of NSS functionality, so
+ * the UI for it can't be controlled by the normal preference bindings.
+ */
+ _initMasterPasswordUI: function ()
+ {
+ var noMP = !LoginHelper.isMasterPasswordSet();
+
+ var button = document.getElementById("changeMasterPassword");
+ button.disabled = noMP;
+
+ var checkbox = document.getElementById("useMasterPassword");
+ checkbox.checked = !noMP;
+ },
+
+ /**
+ * Enables/disables the master password button depending on the state of the
+ * "use master password" checkbox, and prompts for master password removal if
+ * one is set.
+ */
+ updateMasterPasswordButton: function ()
+ {
+ var checkbox = document.getElementById("useMasterPassword");
+ var button = document.getElementById("changeMasterPassword");
+ button.disabled = !checkbox.checked;
+
+ // unchecking the checkbox should try to immediately remove the master
+ // password, because it's impossible to non-destructively remove the master
+ // password used to encrypt all the passwords without providing it (by
+ // design), and it would be extremely odd to pop up that dialog when the
+ // user closes the prefwindow and saves his settings
+ if (!checkbox.checked)
+ this._removeMasterPassword();
+ else
+ this.changeMasterPassword();
+
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Displays the "remove master password" dialog to allow the user to remove
+ * the current master password. When the dialog is dismissed, master password
+ * UI is automatically updated.
+ */
+ _removeMasterPassword: function ()
+ {
+ const Cc = Components.classes, Ci = Components.interfaces;
+ var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].
+ getService(Ci.nsIPKCS11ModuleDB);
+ if (secmodDB.isFIPSEnabled) {
+ var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Ci.nsIPromptService);
+ var bundle = document.getElementById("bundlePreferences");
+ promptService.alert(window,
+ bundle.getString("pw_change_failed_title"),
+ bundle.getString("pw_change2empty_in_fips_mode"));
+ }
+ else {
+ document.documentElement.openSubDialog("chrome://mozapps/content/preferences/removemp.xul",
+ "", null);
+ }
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Displays a dialog in which the master password may be changed.
+ */
+ changeMasterPassword: function ()
+ {
+ document.documentElement.openSubDialog("chrome://mozapps/content/preferences/changemp.xul",
+ "", null);
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Shows the sites where the user has saved passwords and the associated login
+ * information.
+ */
+ showPasswords: function ()
+ {
+ document.documentElement.openWindow("Toolkit:PasswordManager",
+ "chrome://passwordmgr/content/passwordManager.xul",
+ "", null);
+ }
+};
diff --git a/browser/components/preferences/security.xul b/browser/components/preferences/security.xul
new file mode 100644
index 000000000..350eb0d79
--- /dev/null
+++ b/browser/components/preferences/security.xul
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+
+<!-- -*- Mode: Java; tab-width: 4; 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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ <!ENTITY % securityDTD SYSTEM "chrome://browser/locale/preferences/security.dtd">
+ %brandDTD;
+ %securityDTD;
+]>
+
+<overlay id="SecurityPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneSecurity"
+ onpaneload="gSecurityPane.init();"
+ helpTopic="prefs-security">
+
+ <preferences id="securityPreferences">
+ <!-- XXX buttons -->
+ <preference id="pref.privacy.disable_button.view_passwords"
+ name="pref.privacy.disable_button.view_passwords"
+ type="bool"/>
+ <preference id="pref.privacy.disable_button.view_passwords_exceptions"
+ name="pref.privacy.disable_button.view_passwords_exceptions"
+ type="bool"/>
+
+ <!-- Add-ons, malware, phishing -->
+ <preference id="xpinstall.whitelist.required"
+ name="xpinstall.whitelist.required"
+ type="bool"/>
+ <preference id="extensions.blocklist.level"
+ name="extensions.blocklist.level"
+ onchange="gSecurityPane.addonLevelNeedsSync();"
+ type="int"/>
+
+ <!-- Passwords -->
+ <preference id="signon.rememberSignons" name="signon.rememberSignons" type="bool"/>
+ <preference id="signon.autofillForms" name="signon.autofillForms" type="bool"/>
+
+ <!-- Security Protocols -->
+
+ <preference id="network.stricttransportsecurity.enabled"
+ name="network.stricttransportsecurity.enabled"
+ type="bool"/>
+
+ <!-- Opportunistic Encryption -->
+
+ <preference id="network.http.upgrade-insecure-requests"
+ name="network.http.upgrade-insecure-requests"
+ type="bool"/>
+ <preference id="network.http.altsvc.oe"
+ name="network.http.altsvc.oe"
+ type="bool"/>
+
+ <!-- XSS Filter -->
+ <!--
+ <preference id="security.xssfilter.enable" name="security.xssfilter.enable" type="bool"/>
+ -->
+
+ </preferences>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/security.js"/>
+
+ <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <!-- addons, forgery (phishing) UI -->
+ <groupbox id="addonsSecurityGroup">
+ <caption label="&addons.label;"/>
+
+ <hbox id="addonInstallBox">
+ <checkbox id="warnAddonInstall" flex="1"
+ label="&warnAddonInstall.label;"
+ accesskey="&warnAddonInstall.accesskey;"
+ preference="xpinstall.whitelist.required"
+ onsyncfrompreference="return gSecurityPane.readWarnAddonInstall();"/>
+ <button id="addonExceptions"
+ label="&addonExceptions.label;"
+ accesskey="&addonExceptions.accesskey;"
+ oncommand="gSecurityPane.showAddonExceptions();"/>
+ </hbox>
+ <hbox id="addonSecuritySettingsBox" flex="1">
+ <vbox>
+ <label id="addonSecurity" control="addonsecurity-menu">&addonSecuritylevel;</label>
+ <menulist id="addonsecurity-menu" preference="extensions.blocklist.level" sizetopopup="always">
+ <menupopup>
+ <menuitem label="&addonSecurityLevel_Off;" value="99" />
+ <menuitem label="&addonSecurityLevel_Low;" value="3" />
+ <menuitem label="&addonSecurityLevel_High;" value="2" />
+ <menuitem label="&addonSecurityLevel_Extreme;" value="1" />
+ </menupopup>
+ </menulist>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ <!-- Passwords -->
+ <groupbox id="passwordsGroup" orient="vertical">
+ <caption label="&passwords.label;"/>
+
+ <hbox id="savePasswordsBox">
+ <checkbox id="savePasswords" flex="1"
+ label="&rememberPasswords.label;" accesskey="&rememberPasswords.accesskey;"
+ preference="signon.rememberSignons"
+ onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
+ <button id="passwordExceptions"
+ label="&passwordExceptions.label;"
+ accesskey="&passwordExceptions.accesskey;"
+ oncommand="gSecurityPane.showPasswordExceptions();"
+ preference="pref.privacy.disable_button.view_passwords_exceptions"/>
+ </hbox>
+ <checkbox id="autofillPasswords" flex="1"
+ label="&autofillPasswords.label;" accesskey="&autofillPasswords.accesskey;"
+ preference="signon.autofillForms"/>
+ <hbox id="masterPasswordBox">
+ <checkbox id="useMasterPassword" flex="1"
+ oncommand="gSecurityPane.updateMasterPasswordButton();"
+ label="&useMasterPassword.label;"
+ accesskey="&useMasterPassword.accesskey;"/>
+ <button id="changeMasterPassword"
+ label="&changeMasterPassword.label;"
+ accesskey="&changeMasterPassword.accesskey;"
+ oncommand="gSecurityPane.changeMasterPassword();"/>
+ </hbox>
+
+ <hbox id="showPasswordsBox">
+ <spacer flex="1"/>
+ <button id="showPasswords"
+ label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;"
+ oncommand="gSecurityPane.showPasswords();"
+ preference="pref.privacy.disable_button.view_passwords"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Security protocols -->
+ <groupbox id="SecProtoGroup">
+ <caption label="&SecProto.label;"/>
+
+ <vbox id="SecProtoBox" align="start" flex="1">
+ <checkbox id="enableHSTS"
+ label="&enableHSTS.label;"
+ accesskey="&enableHSTS.accesskey;"
+ preference="network.stricttransportsecurity.enabled" />
+ </vbox>
+ </groupbox>
+
+ <groupbox id="OpportunisticEncryption">
+ <caption label="&OpEnc.label;"/>
+ <checkbox id="enableUIROpEnc"
+ label="&enableUIROpEnc.label;"
+ preference="network.http.upgrade-insecure-requests" />
+ <checkbox id="enableAltSvcOpEnc"
+ label="&enableAltSvcOpEnc.label;"
+ preference="network.http.altsvc.oe" />
+ </groupbox>
+
+ <!-- XSS Filter -->
+ <!--
+ <groupbox id="XSSFiltGroup">
+ <caption label="&XSSFilt.label;"/>
+
+ <hbox id="XSSFiltBox">
+ <checkbox id="enableXSSFilt" flex="1"
+ label="&enableXSSFilt.label;"
+ accesskey="&enableXSSFilt.accesskey;"
+ preference="security.xssfilter.enable" />
+ </hbox>
+
+ </groupbox>
+ -->
+
+ </prefpane>
+
+</overlay>
diff --git a/browser/components/preferences/selectBookmark.js b/browser/components/preferences/selectBookmark.js
new file mode 100644
index 000000000..ba468646c
--- /dev/null
+++ b/browser/components/preferences/selectBookmark.js
@@ -0,0 +1,82 @@
+// 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/.
+
+/**
+ * SelectBookmarkDialog controls the user interface for the "Use Bookmark for
+ * Home Page" dialog.
+ *
+ * The caller (gMainPane.setHomePageToBookmark in main.js) invokes this dialog
+ * with a single argument - a reference to an object with a .urls property and
+ * a .names property. This dialog is responsible for updating the contents of
+ * the .urls property with an array of URLs to use as home pages and for
+ * updating the .names property with an array of names for those URLs before it
+ * closes.
+ */
+var SelectBookmarkDialog = {
+ init: function() {
+ document.getElementById("bookmarks").place =
+ "place:queryType=1&folder=" + PlacesUIUtils.allBookmarksFolderId;
+
+ // Initial update of the OK button.
+ this.selectionChanged();
+ },
+
+ /**
+ * Update the disabled state of the OK button as the user changes the
+ * selection within the view.
+ */
+ selectionChanged: function() {
+ var accept = document.documentElement.getButton("accept");
+ var bookmarks = document.getElementById("bookmarks");
+ var disableAcceptButton = true;
+ if (bookmarks.hasSelection) {
+ if (!PlacesUtils.nodeIsSeparator(bookmarks.selectedNode))
+ disableAcceptButton = false;
+ }
+ accept.disabled = disableAcceptButton;
+ },
+
+ onItemDblClick: function() {
+ var bookmarks = document.getElementById("bookmarks");
+ var selectedNode = bookmarks.selectedNode;
+ if (selectedNode && PlacesUtils.nodeIsURI(selectedNode)) {
+ /**
+ * The user has double clicked on a tree row that is a link. Take this to
+ * mean that they want that link to be their homepage, and close the dialog.
+ */
+ document.documentElement.getButton("accept").click();
+ }
+ },
+
+ /**
+ * User accepts their selection. Set all the selected URLs or the contents
+ * of the selected folder as the list of homepages.
+ */
+ accept: function() {
+ var bookmarks = document.getElementById("bookmarks");
+ NS_ASSERT(bookmarks.hasSelection,
+ "Should not be able to accept dialog if there is no selected URL!");
+ var urls = [];
+ var names = [];
+ var selectedNode = bookmarks.selectedNode;
+ if (PlacesUtils.nodeIsFolder(selectedNode)) {
+ var contents = PlacesUtils.getFolderContents(selectedNode.itemId).root;
+ var cc = contents.childCount;
+ for (var i = 0; i < cc; ++i) {
+ var node = contents.getChild(i);
+ if (PlacesUtils.nodeIsURI(node)) {
+ urls.push(node.uri);
+ names.push(node.title);
+ }
+ }
+ contents.containerOpen = false;
+ }
+ else {
+ urls.push(selectedNode.uri);
+ names.push(selectedNode.title);
+ }
+ window.arguments[0].urls = urls;
+ window.arguments[0].names = names;
+ }
+};
diff --git a/browser/components/preferences/selectBookmark.xul b/browser/components/preferences/selectBookmark.xul
new file mode 100644
index 000000000..5547534b6
--- /dev/null
+++ b/browser/components/preferences/selectBookmark.xul
@@ -0,0 +1,44 @@
+<?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://browser/content/places/places.css"?>
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
+
+<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/selectBookmark.dtd">
+
+<dialog id="selectBookmarkDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&selectBookmark.title;" style="width: 32em;"
+ persist="screenX screenY width height" screenX="24" screenY="24"
+ onload="SelectBookmarkDialog.init();"
+ ondialogaccept="SelectBookmarkDialog.accept();">
+
+ <script type="application/javascript"
+ src="chrome://browser/content/preferences/selectBookmark.js"/>
+
+ <description>&selectBookmark.label;</description>
+
+ <separator class="thin"/>
+
+ <tree id="bookmarks" flex="1" type="places"
+ style="height: 15em;"
+ hidecolumnpicker="true"
+ seltype="single"
+ ondblclick="SelectBookmarkDialog.onItemDblClick();"
+ onselect="SelectBookmarkDialog.selectionChanged();">
+ <treecols>
+ <treecol id="title" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren id="bookmarksChildren" flex="1"/>
+ </tree>
+
+ <separator class="thin"/>
+
+</dialog>
diff --git a/browser/components/preferences/sync.js b/browser/components/preferences/sync.js
new file mode 100644
index 000000000..ecf4fe6ef
--- /dev/null
+++ b/browser/components/preferences/sync.js
@@ -0,0 +1,192 @@
+// 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://services-sync/main.js");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const PAGE_NO_ACCOUNT = 0;
+const PAGE_HAS_ACCOUNT = 1;
+const PAGE_NEEDS_UPDATE = 2;
+
+var gSyncPane = {
+ _stringBundle: null,
+ prefArray: ["engine.bookmarks", "engine.passwords", "engine.prefs",
+ "engine.tabs", "engine.history"],
+
+ get page() {
+ return document.getElementById("weavePrefsDeck").selectedIndex;
+ },
+
+ set page(val) {
+ document.getElementById("weavePrefsDeck").selectedIndex = val;
+ },
+
+ get _usingCustomServer() {
+ return Weave.Svc.Prefs.isSet("serverURL");
+ },
+
+ needsUpdate: function () {
+ this.page = PAGE_NEEDS_UPDATE;
+ let label = document.getElementById("loginError");
+ label.value = Weave.Utils.getErrorString(Weave.Status.login);
+ label.className = "error";
+ },
+
+ init: function () {
+ // If the Service hasn't finished initializing, wait for it.
+ let xps = Components.classes["@mozilla.org/weave/service;1"]
+ .getService(Components.interfaces.nsISupports)
+ .wrappedJSObject;
+
+ if (xps.ready) {
+ this._init();
+ return;
+ }
+
+ let onUnload = function () {
+ window.removeEventListener("unload", onUnload, false);
+ try {
+ Services.obs.removeObserver(onReady, "weave:service:ready");
+ } catch (e) {}
+ };
+
+ let onReady = function () {
+ Services.obs.removeObserver(onReady, "weave:service:ready");
+ window.removeEventListener("unload", onUnload, false);
+ this._init();
+ }.bind(this);
+
+ Services.obs.addObserver(onReady, "weave:service:ready", false);
+ window.addEventListener("unload", onUnload, false);
+
+ xps.ensureLoaded();
+ },
+
+ _init: function () {
+ let topics = ["weave:service:login:error",
+ "weave:service:login:finish",
+ "weave:service:start-over",
+ "weave:service:setup-complete",
+ "weave:service:logout:finish"];
+
+ // Add the observers now and remove them on unload
+ //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
+ // of `this`. Fix in a followup. (bug 583347)
+ topics.forEach(function (topic) {
+ Weave.Svc.Obs.add(topic, this.updateWeavePrefs, this);
+ }, this);
+ window.addEventListener("unload", function() {
+ topics.forEach(function (topic) {
+ Weave.Svc.Obs.remove(topic, this.updateWeavePrefs, this);
+ }, gSyncPane);
+ }, false);
+
+ this._stringBundle =
+ Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
+ this.updateWeavePrefs();
+ },
+
+ updateWeavePrefs: function () {
+ if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
+ Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
+ this.page = PAGE_NO_ACCOUNT;
+ } else if (Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE ||
+ Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
+ this.needsUpdate();
+ } else {
+ this.page = PAGE_HAS_ACCOUNT;
+ document.getElementById("accountName").value = Weave.Service.identity.account;
+ document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
+ document.getElementById("tosPP").hidden = this._usingCustomServer;
+ }
+ },
+
+ startOver: function (showDialog) {
+ if (showDialog) {
+ let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
+ Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL +
+ Services.prompt.BUTTON_POS_1_DEFAULT;
+ let buttonChoice =
+ Services.prompt.confirmEx(window,
+ this._stringBundle.GetStringFromName("syncUnlink.title"),
+ this._stringBundle.GetStringFromName("syncUnlink.label"),
+ flags,
+ this._stringBundle.GetStringFromName("syncUnlinkConfirm.label"),
+ null, null, null, {});
+
+ // If the user selects cancel, just bail
+ if (buttonChoice == 1) {
+ return;
+ }
+ }
+
+ Weave.Service.startOver();
+ this.updateWeavePrefs();
+ },
+
+ updatePass: function () {
+ if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
+ gSyncUtils.changePassword();
+ } else {
+ gSyncUtils.updatePassphrase();
+ }
+ },
+
+ resetPass: function () {
+ if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
+ gSyncUtils.resetPassword();
+ } else {
+ gSyncUtils.resetPassphrase();
+ }
+ },
+
+ /**
+ * Invoke the Sync setup wizard.
+ *
+ * @param wizardType
+ * Indicates type of wizard to launch:
+ * null -- regular set up wizard
+ * "pair" -- pair a device first
+ * "reset" -- reset sync
+ */
+ openSetup: function (wizardType) {
+ let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
+ if (win) {
+ win.focus();
+ } else {
+ window.openDialog("chrome://weave/content/setup.xul",
+ "weaveSetup", "centerscreen,chrome,resizable=no",
+ wizardType);
+ }
+ },
+
+ openQuotaDialog: function () {
+ let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
+ if (win) {
+ win.focus();
+ } else {
+ window.openDialog("chrome://weave/content/quota.xul", "",
+ "centerscreen,chrome,dialog,modal");
+ }
+ },
+
+ openAddDevice: function () {
+ if (!Weave.Utils.ensureMPUnlocked()) {
+ return;
+ }
+
+ let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
+ if (win) {
+ win.focus();
+ } else {
+ window.openDialog("chrome://weave/content/addDevice.xul",
+ "syncAddDevice", "centerscreen,chrome,resizable=no");
+ }
+ },
+
+ resetSync: function () {
+ this.openSetup("reset");
+ },
+};
+
diff --git a/browser/components/preferences/sync.xul b/browser/components/preferences/sync.xul
new file mode 100644
index 000000000..2c91e0cd5
--- /dev/null
+++ b/browser/components/preferences/sync.xul
@@ -0,0 +1,178 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://branding/locale/syncBrand.dtd">
+<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncDTD;
+]>
+
+<overlay id="SyncPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <prefpane id="paneSync"
+ helpTopic="prefs-weave"
+ onpaneload="gSyncPane.init()">
+
+ <preferences>
+<!-- <preference id="engine.addons" name="services.sync.engine.addons" type="bool"/> -->
+ <preference id="engine.bookmarks" name="services.sync.engine.bookmarks" type="bool"/>
+ <preference id="engine.history" name="services.sync.engine.history" type="bool"/>
+ <preference id="engine.tabs" name="services.sync.engine.tabs" type="bool"/>
+ <preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/>
+ <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/>
+ </preferences>
+
+
+ <script type="application/javascript"
+ src="chrome://browser/content/preferences/sync.js"/>
+ <script type="application/javascript"
+ src="chrome://weave/content/utils.js"/>
+
+
+ <deck id="weavePrefsDeck">
+ <vbox id="noAccount" align="center">
+ <spacer flex="1"/>
+ <description id="syncDesc">
+ &weaveDesc.label;
+ </description>
+ <separator/>
+ <label class="text-link"
+ onclick="event.stopPropagation(); gSyncPane.openSetup(null);"
+ value="&setupButton.label;"/>
+ <separator/>
+ <label class="text-link"
+ onclick="event.stopPropagation(); gSyncPane.openSetup('pair');"
+ value="&pairDevice.label;"/>
+ <spacer flex="3"/>
+ </vbox>
+
+ <vbox id="hasAccount">
+ <groupbox class="syncGroupBox">
+ <!-- label is set to account name -->
+ <caption id="accountCaption" align="center">
+ <image id="accountCaptionImage"/>
+ <label id="accountName" value=""/>
+ </caption>
+
+ <hbox>
+ <button type="menu"
+ label="&manageAccount.label;"
+ accesskey="&manageAccount.accesskey;">
+ <menupopup>
+ <menuitem label="&viewQuota.label;"
+ oncommand="gSyncPane.openQuotaDialog();"/>
+ <menuseparator/>
+ <menuitem label="&changePassword2.label;"
+ oncommand="gSyncUtils.changePassword();"/>
+ <menuitem label="&myRecoveryKey.label;"
+ oncommand="gSyncUtils.resetPassphrase();"/>
+ <menuseparator/>
+ <menuitem label="&resetSync2.label;"
+ oncommand="gSyncPane.resetSync();"/>
+ </menupopup>
+ </button>
+ </hbox>
+
+ <hbox>
+ <label id="syncAddDeviceLabel"
+ class="text-link"
+ onclick="gSyncPane.openAddDevice(); return false;"
+ value="&pairDevice.label;"/>
+ </hbox>
+
+ <vbox>
+ <label value="&syncMy.label;" />
+ <richlistbox id="syncEnginesList"
+ orient="vertical"
+ onselect="if (this.selectedCount) this.clearSelection();">
+<!-- <richlistitem>
+ <checkbox label="&engine.addons.label;"
+ accesskey="&engine.addons.accesskey;"
+ preference="engine.addons"/>
+ </richlistitem> -->
+ <richlistitem>
+ <checkbox label="&engine.bookmarks.label;"
+ accesskey="&engine.bookmarks.accesskey;"
+ preference="engine.bookmarks"/>
+ </richlistitem>
+ <richlistitem>
+ <checkbox label="&engine.passwords.label;"
+ accesskey="&engine.passwords.accesskey;"
+ preference="engine.passwords"/>
+ </richlistitem>
+ <richlistitem>
+ <checkbox label="&engine.prefs.label;"
+ accesskey="&engine.prefs.accesskey;"
+ preference="engine.prefs"/>
+ </richlistitem>
+ <richlistitem>
+ <checkbox label="&engine.history.label;"
+ accesskey="&engine.history.accesskey;"
+ preference="engine.history"/>
+ </richlistitem>
+ <richlistitem>
+ <checkbox label="&engine.tabs.label;"
+ accesskey="&engine.tabs.accesskey;"
+ preference="engine.tabs"/>
+ </richlistitem>
+ </richlistbox>
+ </vbox>
+ </groupbox>
+
+ <groupbox class="syncGroupBox">
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&syncDeviceName.label;"
+ accesskey="&syncDeviceName.accesskey;"
+ control="syncComputerName"/>
+ <textbox id="syncComputerName"
+ onchange="gSyncUtils.changeName(this)"/>
+ </row>
+ </rows>
+ </grid>
+ <hbox>
+ <label class="text-link"
+ onclick="gSyncPane.startOver(true); return false;"
+ value="&unlinkDevice.label;"/>
+ </hbox>
+ </groupbox>
+ <hbox id="tosPP" pack="center">
+ <label class="text-link"
+ onclick="event.stopPropagation();gSyncUtils.openToS();"
+ value="&prefs.tosLink.label;"/>
+ <label class="text-link"
+ onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"
+ value="&prefs.ppLink.label;"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="needsUpdate" align="center" pack="center">
+ <hbox>
+ <label id="loginError" value=""/>
+ <label class="text-link"
+ onclick="gSyncPane.updatePass(); return false;"
+ value="&updatePass.label;"/>
+ <label class="text-link"
+ onclick="gSyncPane.resetPass(); return false;"
+ value="&resetPass.label;"/>
+ </hbox>
+ <label class="text-link"
+ onclick="gSyncPane.startOver(true); return false;"
+ value="&unlinkDevice.label;"/>
+ </vbox>
+ </deck>
+ </prefpane>
+</overlay>
diff --git a/browser/components/preferences/tabs.js b/browser/components/preferences/tabs.js
new file mode 100644
index 000000000..811064291
--- /dev/null
+++ b/browser/components/preferences/tabs.js
@@ -0,0 +1,89 @@
+// 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 gTabsPane = {
+
+ /*
+ * Preferences:
+ *
+ * browser.link.open_newwindow
+ * - determines where pages which would open in a new window are opened:
+ * 1 opens such links in the most recent window or tab,
+ * 2 opens such links in a new window,
+ * 3 opens such links in a new tab
+ * browser.tabs.loadInBackground
+ * - true if display should switch to a new tab which has been opened from a
+ * link, false if display shouldn't switch
+ * browser.tabs.warnOnClose
+ * - true if when closing a window with multiple tabs the user is warned and
+ * allowed to cancel the action, false to just close the window
+ * browser.tabs.warnOnOpen
+ * - true if the user should be warned if he attempts to open a lot of tabs at
+ * once (e.g. a large folder of bookmarks), false otherwise
+ * browser.taskbar.previews.enable
+ * - true if tabs are to be shown in the Windows 7 taskbar
+ */
+
+ /**
+ * Initialize any platform-specific UI.
+ */
+ init: function () {
+#ifdef XP_WIN
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+ try {
+ let sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ let ver = parseFloat(sysInfo.getProperty("version"));
+ let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
+ showTabsInTaskbar.hidden = ver < 6.1;
+ } catch (ex) {}
+#endif
+ // Set the proper value in the newtab drop-down.
+ gTabsPane.readNewtabUrl();
+ },
+
+ /**
+ * Pale Moon: synchronize warnOnClose and warnOnCloseOtherTabs
+ */
+ syncWarnOnClose: function() {
+ var warnOnClosePref = document.getElementById("browser.tabs.warnOnClose");
+ var warnOnCloseOtherPref = document.getElementById("browser.tabs.warnOnCloseOtherTabs");
+ warnOnCloseOtherPref.value = warnOnClosePref.value;
+ },
+
+ /**
+ * Determines where a link which opens a new window will open.
+ *
+ * @returns |true| if such links should be opened in new tabs
+ */
+ readLinkTarget: function() {
+ var openNewWindow = document.getElementById("browser.link.open_newwindow");
+ return openNewWindow.value != 2;
+ },
+
+ /**
+ * Determines where a link which opens a new window will open.
+ *
+ * @returns 2 if such links should be opened in new windows,
+ * 3 if such links should be opened in new tabs
+ */
+ writeLinkTarget: function() {
+ var linkTargeting = document.getElementById("linkTargeting");
+ return linkTargeting.checked ? 3 : 2;
+ },
+
+ /**
+ * Determines the value of the New Tab display drop-down based
+ * on the value of browser.newtab.url.
+ */
+ readNewtabUrl: function() {
+ let newtabUrlChoice = document.getElementById("browser.newtab.choice");
+ newtabUrlChoice.value = gNewtabUrl.getNewtabChoice();
+ if (newtabUrlChoice.value == 0) {
+ document.getElementById("newtabPageCustom").hidden = false;
+ }
+ gNewtabUrl.newtabUrlChoiceIsSet = true;
+ }
+};
diff --git a/browser/components/preferences/tabs.xul b/browser/components/preferences/tabs.xul
new file mode 100644
index 000000000..1f7a2a9e3
--- /dev/null
+++ b/browser/components/preferences/tabs.xul
@@ -0,0 +1,101 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
+%tabsDTD;
+]>
+
+<overlay id="TabsPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="paneTabs"
+ onpaneload="gTabsPane.init();"
+ helpTopic="prefs-tabs">
+
+ <preferences id="tabsPreferences">
+ <preference id="browser.link.open_newwindow" name="browser.link.open_newwindow" type="int"/>
+ <preference id="browser.tabs.autoHide" name="browser.tabs.autoHide" type="bool" inverted="true"/>
+ <preference id="browser.tabs.loadInBackground" name="browser.tabs.loadInBackground" type="bool" inverted="true"/>
+ <preference id="browser.tabs.warnOnClose" name="browser.tabs.warnOnClose" type="bool"
+ onchange="gTabsPane.syncWarnOnClose();"/>
+ <preference id="browser.tabs.warnOnCloseOtherTabs" name="browser.tabs.warnOnCloseOtherTabs" type="bool"/>
+ <preference id="browser.tabs.warnOnOpen" name="browser.tabs.warnOnOpen" type="bool"/>
+ <preference id="browser.sessionstore.restore_on_demand" name="browser.sessionstore.restore_on_demand" type="bool"/>
+#ifdef XP_WIN
+ <preference id="browser.taskbar.previews.enable" name="browser.taskbar.previews.enable" type="bool"/>
+#endif
+ <preference id="browser.tabs.insertRelatedAfterCurrent" name="browser.tabs.insertRelatedAfterCurrent" type="bool"/>
+ <preference id="browser.search.context.loadInBackground" name="browser.search.context.loadInBackground" type="bool" inverted="true"/>
+ <preference id="browser.tabs.closeWindowWithLastTab" name="browser.tabs.closeWindowWithLastTab" type="bool"/>
+ <preference id="browser.ctrlTab.previews" name="browser.ctrlTab.previews" type="bool"/>
+
+ <preference id="browser.newtab.url" name="browser.newtab.url" type="string"/>
+ <preference id="browser.newtab.myhome" name="browser.newtab.myhome" type="string"/>
+ <preference id="browser.newtab.choice" name="browser.newtab.choice" type="int"/>
+ </preferences>
+
+ <script type="application/javascript" src="chrome://browser/content/preferences/tabs.js"/>
+
+ <!-- XXX flex below is a hack because wrapping checkboxes don't reflow
+ properly; see bug 349098 -->
+ <vbox id="tabPrefsBox" align="start" flex="1">
+ <checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
+ accesskey="&newWindowsAsTabs.accesskey;"
+ preference="browser.link.open_newwindow"
+ onsyncfrompreference="return gTabsPane.readLinkTarget();"
+ onsynctopreference="return gTabsPane.writeLinkTarget();"/>
+ <checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
+ accesskey="&warnCloseMultipleTabs.accesskey;"
+ preference="browser.tabs.warnOnClose"/>
+ <checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
+ accesskey="&warnOpenManyTabs.accesskey;"
+ preference="browser.tabs.warnOnOpen"/>
+ <checkbox id="showTabBar" label="&showTabBar.label;"
+ accesskey="&showTabBar.accesskey;"
+ preference="browser.tabs.autoHide"/>
+ <checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
+ accesskey="&restoreTabsOnDemand.accesskey;"
+ preference="browser.sessionstore.restore_on_demand"/>
+ <checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
+ accesskey="&switchToNewTabs.accesskey;"
+ preference="browser.tabs.loadInBackground"/>
+#ifdef XP_WIN
+ <checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
+ accesskey="&showTabsInTaskbar.accesskey;"
+ preference="browser.taskbar.previews.enable"/>
+#endif
+<!-- Pale Moon additions -->
+ <checkbox id="insertRelatedAfterCurrent" label="&insertRelatedAfterCurrent.label;"
+ preference="browser.tabs.insertRelatedAfterCurrent"/>
+ <checkbox id="contextLoadInBackground" label="&contextLoadInBackground.label;"
+ preference="browser.search.context.loadInBackground"/>
+ <checkbox id="closeWindowWithLastTab" label="&closeWindowWithLastTab.label;"
+ preference="browser.tabs.closeWindowWithLastTab"/>
+ <checkbox id="showTabPreviews" label="&showTabPreviews.label;"
+ preference="browser.ctrlTab.previews"/>
+ <hbox align="center">
+ <label value="&newtabPage.label;"/>
+ <menulist
+ id="newtabPage"
+ preference="browser.newtab.choice"
+ oncommand="gNewtabUrl.writeNewtabUrl(event.target.value);">
+ <menupopup>
+ <menuitem label="&newtabPage.custom.label;" value="0" id="newtabPageCustom" hidden="true" />
+ <menuitem label="&newtabPage.blank.label;" value="1" />
+ <menuitem label="&newtabPage.home.label;" value="2" />
+ <menuitem label="&newtabPage.myhome.label;" value="3" />
+ <menuitem label="&newtabPage.quickdial.label;" value="4" />
+ </menupopup>
+ </menulist>
+ </hbox>
+ </vbox>
+
+ </prefpane>
+
+</overlay>