diff options
24 files changed, 298 insertions, 13 deletions
diff --git a/application/palemoon/components/nsBrowserContentHandler.js b/application/palemoon/components/nsBrowserContentHandler.js index 13ea9da12..6a75b40f2 100644 --- a/application/palemoon/components/nsBrowserContentHandler.js +++ b/application/palemoon/components/nsBrowserContentHandler.js @@ -518,16 +518,16 @@ nsBrowserContentHandler.prototype = { #endif }, - helpInfo : " -browser Open a browser window.\n" + - " -new-window <url> Open <url> in a new window.\n" + - " -new-tab <url> Open <url> in a new tab.\n" + - " -private-window <url> Open <url> in a new private window.\n" + + helpInfo : " --browser Open a browser window.\n" + + " --new-window <url> Open <url> in a new window.\n" + + " --new-tab <url> Open <url> in a new tab.\n" + + " --private-window <url> Open <url> in a new private window.\n" + #ifdef XP_WIN - " -preferences Open Options dialog.\n" + + " --preferences Open Options dialog.\n" + #else - " -preferences Open Preferences dialog.\n" + + " --preferences Open Preferences dialog.\n" + #endif - " -search <term> Search <term> with your default search engine.\n", + " --search <term> Search <term> with your default search engine.\n", /* nsIBrowserHandler */ diff --git a/toolkit/components/xulstore/XULStore.js b/toolkit/components/xulstore/XULStore.js index c2721327c..8b5bc1313 100644 --- a/toolkit/components/xulstore/XULStore.js +++ b/toolkit/components/xulstore/XULStore.js @@ -63,11 +63,21 @@ XULStore.prototype = { load: function () { Services.obs.addObserver(this, "profile-before-change", true); - this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile); + let profileType = "ProfD"; + try { + this._storeFile = Services.dirsvc.get(profileType, Ci.nsIFile); + } catch (ex) { + try { + profileType = "ProfDS"; + this._storeFile = Services.dirsvc.get(profileType, Ci.nsIFile); + } catch (ex) { + throw new Error("Can't find profile directory."); + } + } this._storeFile.append(STOREDB_FILENAME); if (!this._storeFile.exists()) { - this.import(); + this.import(profileType); } else { this.readFile(); } @@ -90,8 +100,8 @@ XULStore.prototype = { Services.console.logStringMessage("XULStore: " + message); }, - import: function() { - let localStoreFile = Services.dirsvc.get("ProfD", Ci.nsIFile); + import(profileType) { + let localStoreFile = Services.dirsvc.get(profileType || "ProfD", Ci.nsIFile); localStoreFile.append("localstore.rdf"); if (!localStoreFile.exists()) { diff --git a/toolkit/jetpack/modules/system/Startup.js b/toolkit/jetpack/modules/system/Startup.js index b9e5d88b3..89eeac3bc 100644 --- a/toolkit/jetpack/modules/system/Startup.js +++ b/toolkit/jetpack/modules/system/Startup.js @@ -15,6 +15,7 @@ const appStartupSrv = Cc["@mozilla.org/toolkit/app-startup;1"] .getService(Ci.nsIAppStartup); const NAME2TOPIC = { + 'Palemoon': 'sessionstore-windows-restored', 'Firefox': 'sessionstore-windows-restored', 'Fennec': 'sessionstore-windows-restored', 'SeaMonkey': 'sessionstore-windows-restored', diff --git a/toolkit/jetpack/moz.build b/toolkit/jetpack/moz.build index 2024e6a7c..ad11e90a3 100644 --- a/toolkit/jetpack/moz.build +++ b/toolkit/jetpack/moz.build @@ -85,10 +85,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk": 'sdk/ui/toolbar.js', ] + if CONFIG['MC_PALEMOON']: + EXTRA_JS_MODULES.commonjs.sdk.ui += [ + 'sdk/ui/buttons.js', + ] + EXTRA_JS_MODULES.commonjs.sdk.ui.button += [ 'sdk/ui/button/action.js', 'sdk/ui/button/contract.js', 'sdk/ui/button/toggle.js', + ] + + EXTRA_PP_JS_MODULES.commonjs.sdk.ui.button += [ 'sdk/ui/button/view.js', ] @@ -211,11 +219,14 @@ EXTRA_JS_MODULES.commonjs.sdk += [ 'sdk/tabs.js', 'sdk/test.js', 'sdk/timers.js', - 'sdk/ui.js', 'sdk/url.js', 'sdk/windows.js', ] +EXTRA_PP_JS_MODULES.commonjs.sdk += [ + 'sdk/ui.js', +] + EXTRA_JS_MODULES.commonjs.sdk.addon += [ 'sdk/addon/bootstrap.js', 'sdk/addon/events.js', diff --git a/toolkit/jetpack/sdk/clipboard.js b/toolkit/jetpack/sdk/clipboard.js index 048d5f2f1..c6b3c46fe 100644 --- a/toolkit/jetpack/sdk/clipboard.js +++ b/toolkit/jetpack/sdk/clipboard.js @@ -8,6 +8,7 @@ module.metadata = { "stability": "stable", "engines": { // TODO Fennec Support 789757 + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*", "Thunderbird": "*" diff --git a/toolkit/jetpack/sdk/context-menu.js b/toolkit/jetpack/sdk/context-menu.js index 004c642d4..e00f41d79 100644 --- a/toolkit/jetpack/sdk/context-menu.js +++ b/toolkit/jetpack/sdk/context-menu.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "stable", "engines": { // TODO Fennec support Bug 788334 + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/bookmarks.js b/toolkit/jetpack/sdk/places/bookmarks.js index c4f9528f1..e8fdae4f4 100644 --- a/toolkit/jetpack/sdk/places/bookmarks.js +++ b/toolkit/jetpack/sdk/places/bookmarks.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "unstable", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/events.js b/toolkit/jetpack/sdk/places/events.js index a3f95ee03..c5e728039 100644 --- a/toolkit/jetpack/sdk/places/events.js +++ b/toolkit/jetpack/sdk/places/events.js @@ -7,6 +7,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '*', 'Firefox': '*', "SeaMonkey": '*' } diff --git a/toolkit/jetpack/sdk/places/favicon.js b/toolkit/jetpack/sdk/places/favicon.js index 05b057db1..7a74aa517 100644 --- a/toolkit/jetpack/sdk/places/favicon.js +++ b/toolkit/jetpack/sdk/places/favicon.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "unstable", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/history.js b/toolkit/jetpack/sdk/places/history.js index b243b024c..f7fc3ed57 100644 --- a/toolkit/jetpack/sdk/places/history.js +++ b/toolkit/jetpack/sdk/places/history.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "unstable", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/host/host-bookmarks.js b/toolkit/jetpack/sdk/places/host/host-bookmarks.js index 3245c4070..f6dec4069 100644 --- a/toolkit/jetpack/sdk/places/host/host-bookmarks.js +++ b/toolkit/jetpack/sdk/places/host/host-bookmarks.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "experimental", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/host/host-query.js b/toolkit/jetpack/sdk/places/host/host-query.js index f2dbd6550..a2cd4cd35 100644 --- a/toolkit/jetpack/sdk/places/host/host-query.js +++ b/toolkit/jetpack/sdk/places/host/host-query.js @@ -6,6 +6,7 @@ module.metadata = { "stability": "experimental", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/host/host-tags.js b/toolkit/jetpack/sdk/places/host/host-tags.js index 929a5d5af..b94342549 100644 --- a/toolkit/jetpack/sdk/places/host/host-tags.js +++ b/toolkit/jetpack/sdk/places/host/host-tags.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "experimental", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/places/utils.js b/toolkit/jetpack/sdk/places/utils.js index 44366d2aa..fe928c4ea 100644 --- a/toolkit/jetpack/sdk/places/utils.js +++ b/toolkit/jetpack/sdk/places/utils.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "experimental", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/selection.js b/toolkit/jetpack/sdk/selection.js index 8682e8c6d..e393aae06 100644 --- a/toolkit/jetpack/sdk/selection.js +++ b/toolkit/jetpack/sdk/selection.js @@ -7,6 +7,7 @@ module.metadata = { "stability": "stable", "engines": { + "Palemoon": "*", "Firefox": "*", "SeaMonkey": "*" } diff --git a/toolkit/jetpack/sdk/system/xul-app.jsm b/toolkit/jetpack/sdk/system/xul-app.jsm index 90681bb1b..ed760c3a4 100644 --- a/toolkit/jetpack/sdk/system/xul-app.jsm +++ b/toolkit/jetpack/sdk/system/xul-app.jsm @@ -41,6 +41,7 @@ var platformVersion = exports.platformVersion = appInfo.platformVersion; // GUID. var ids = exports.ids = { + Palemoon: "{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}", Firefox: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", Mozilla: "{86c18b42-e466-45a9-ae7a-9b95ba6f5640}", SeaMonkey: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}", diff --git a/toolkit/jetpack/sdk/ui.js b/toolkit/jetpack/sdk/ui.js index 7f9110b26..d1ff7ceb8 100644 --- a/toolkit/jetpack/sdk/ui.js +++ b/toolkit/jetpack/sdk/ui.js @@ -6,12 +6,15 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '> 27', 'Firefox': '> 28' } }; exports.ActionButton = require('./ui/button/action').ActionButton; exports.ToggleButton = require('./ui/button/toggle').ToggleButton; +#ifndef MC_PALEMOON exports.Sidebar = require('./ui/sidebar').Sidebar; exports.Frame = require('./ui/frame').Frame; exports.Toolbar = require('./ui/toolbar').Toolbar; +#endif diff --git a/toolkit/jetpack/sdk/ui/button/action.js b/toolkit/jetpack/sdk/ui/button/action.js index dfb092d0c..5355705be 100644 --- a/toolkit/jetpack/sdk/ui/button/action.js +++ b/toolkit/jetpack/sdk/ui/button/action.js @@ -6,6 +6,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '> 27', 'Firefox': '> 28' } }; diff --git a/toolkit/jetpack/sdk/ui/button/toggle.js b/toolkit/jetpack/sdk/ui/button/toggle.js index a226b3212..d8a3d1758 100644 --- a/toolkit/jetpack/sdk/ui/button/toggle.js +++ b/toolkit/jetpack/sdk/ui/button/toggle.js @@ -6,6 +6,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '> 27', 'Firefox': '> 28' } }; diff --git a/toolkit/jetpack/sdk/ui/button/view.js b/toolkit/jetpack/sdk/ui/button/view.js index 63b7aea31..552aab2f7 100644 --- a/toolkit/jetpack/sdk/ui/button/view.js +++ b/toolkit/jetpack/sdk/ui/button/view.js @@ -6,6 +6,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '> 27', 'Firefox': '> 28' } }; @@ -19,14 +20,19 @@ const { isObject, isNil } = require('../../lang/type'); const { getMostRecentBrowserWindow } = require('../../window/utils'); const { ignoreWindow } = require('../../private-browsing/utils'); +#ifdef MC_PALEMOON +const { buttons } = require('../buttons'); +#else const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); const { AREA_PANEL, AREA_NAVBAR } = CustomizableUI; +#endif const { events: viewEvents } = require('./view/events'); const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; const views = new Map(); +#ifndef MC_PALEMOON const customizedWindows = new WeakMap(); const buttonListener = { @@ -64,19 +70,25 @@ CustomizableUI.addListener(buttonListener); require('../../system/unload').when( _ => CustomizableUI.removeListener(buttonListener) ); +#endif function getNode(id, window) { return !views.has(id) || ignoreWindow(window) ? null +#ifdef MC_PALEMOON + : buttons.getNode(id, window); +#else : CustomizableUI.getWidget(id).forWindow(window).node +#endif }; +#ifndef MC_PALEMOON function isInToolbar(id) { let placement = CustomizableUI.getPlacementOfWidget(id); return placement && CustomizableUI.getAreaType(placement.area) === 'toolbar'; } - +#endif function getImage(icon, isInToolbar, pixelRatio) { let targetSize = (isInToolbar ? 18 : 32) * pixelRatio; @@ -109,7 +121,11 @@ function getImage(icon, isInToolbar, pixelRatio) { } function nodeFor(id, window=getMostRecentBrowserWindow()) { +#ifdef MC_PALEMOON + return getNode(id, window); +#else return customizedWindows.has(window) ? null : getNode(id, window); +#endif }; exports.nodeFor = nodeFor; @@ -119,14 +135,23 @@ function create(options) { if (views.has(id)) throw new Error('The ID "' + id + '" seems already used.'); +#ifdef MC_PALEMOON + buttons.createButton({ +#else CustomizableUI.createWidget({ +#endif id: id, +#ifdef MC_PALEMOON + + onBuild: function(document, _id) { +#else type: 'custom', removable: true, defaultArea: AREA_NAVBAR, allowedAreas: [ AREA_PANEL, AREA_NAVBAR ], onBuild: function(document) { +#endif let window = document.defaultView; let node = document.createElementNS(XUL_NS, 'toolbarbutton'); @@ -136,16 +161,28 @@ function create(options) { if (ignoreWindow(window)) node.style.display = 'none'; +#ifdef MC_PALEMOON + node.setAttribute('id', _id); +#else node.setAttribute('id', this.id); +#endif node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional badged-button'); +#ifndef MC_PALEMOON node.setAttribute('type', type); +#endif node.setAttribute('label', label); node.setAttribute('tooltiptext', label); node.setAttribute('image', image); +#ifdef MC_PALEMOON + node.setAttribute('pmkit-button', 'true'); +#else node.setAttribute('constrain-size', 'true'); +#endif views.set(id, { +#ifndef MC_PALEMOON area: this.currentArea, +#endif icon: icon, label: label }); @@ -171,7 +208,11 @@ function dispose(id) { if (!views.has(id)) return; views.delete(id); +#ifdef MC_PALEMOON + buttons.destroyButton(id); +#else CustomizableUI.destroyWidget(id); +#endif } exports.dispose = dispose; @@ -179,8 +220,12 @@ function setIcon(id, window, icon) { let node = getNode(id, window); if (node) { +#ifdef MC_PALEMOON + let image = getImage(icon, true, window.devicePixelRatio); +#else icon = customizedWindows.has(window) ? views.get(id).icon : icon; let image = getImage(icon, isInToolbar(id), window.devicePixelRatio); +#endif node.setAttribute('image', image); } diff --git a/toolkit/jetpack/sdk/ui/button/view/events.js b/toolkit/jetpack/sdk/ui/button/view/events.js index 98909656a..34d14be3a 100644 --- a/toolkit/jetpack/sdk/ui/button/view/events.js +++ b/toolkit/jetpack/sdk/ui/button/view/events.js @@ -7,6 +7,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '*', 'Firefox': '*', 'SeaMonkey': '*', 'Thunderbird': '*' diff --git a/toolkit/jetpack/sdk/ui/buttons.js b/toolkit/jetpack/sdk/ui/buttons.js new file mode 100644 index 000000000..66e0fd742 --- /dev/null +++ b/toolkit/jetpack/sdk/ui/buttons.js @@ -0,0 +1,198 @@ +/* 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/. + * PMkit shim for 'sdk/ui/button', (c) JustOff, 2017 */ +"use strict"; + +module.metadata = { + "stability": "experimental", + "engines": { + "Palemoon": "> 27" + } +}; + +const { Ci, Cc } = require('chrome'); +const prefs = require('../preferences/service'); + +const buttonsList = new Map(); +const LOCATION_PREF_ROOT = "extensions.sdk-button-location."; + +let gWindowListener; + +function getLocation(id) { + let toolbarId = "nav-bar", nextItemId = ""; + let location = prefs.get(LOCATION_PREF_ROOT + id); + if (location && location.indexOf(",") !== -1) { + [toolbarId, nextItemId] = location.split(","); + } + return [toolbarId, nextItemId]; +} + +function saveLocation(id, toolbarId, nextItemId) { + let _toolbarId = toolbarId || ""; + let _nextItemId = nextItemId || ""; + prefs.set(LOCATION_PREF_ROOT + id, [_toolbarId, _nextItemId].join(",")); +} + +// Insert button into window +function insertButton(aWindow, id, onBuild) { + // Build button and save reference to it + let doc = aWindow.document; + let b = onBuild(doc, id); + aWindow[id] = b; + + // Add to the customization palette + let toolbox = doc.getElementById("navigator-toolbox"); + toolbox.palette.appendChild(b); + + // Retrieve button location from preferences + let [toolbarId, nextItemId] = getLocation(id); + let toolbar = toolbarId != "" && doc.getElementById(toolbarId); + + if (toolbar) { + let nextItem = doc.getElementById(nextItemId); + // If nextItem not in toolbar then retrieve it by reading currentset attribute + if (!(nextItem && nextItem.parentNode && nextItem.parentNode.id == toolbarId)) { + nextItem = null; + let currentSet = toolbar.getAttribute("currentset"); + let ids = (currentSet == "__empty") ? [] : currentSet.split(","); + let idx = ids.indexOf(id); + if (idx != -1) { + for (let i = idx; i < ids.length; i++) { + nextItem = doc.getElementById(ids[i]); + if (nextItem) + break; + } + } + } + // Finally insert button in the right toolbar and in the right position + toolbar.insertItem(id, nextItem, null, false); + } +} + +// Remove button from window +function removeButton(aWindow, id) { + let b = aWindow[id]; + b.parentNode.removeChild(b); + delete aWindow[id]; +} + +// Save locations of buttons after customization +function afterCustomize(e) { + for (let [id] of buttonsList) { + let toolbox = e.target; + let b = toolbox.parentNode.querySelector("#" + id); + let toolbarId = null, nextItemId = null; + if (b) { + let parent = b.parentNode; + let nextItem = b.nextSibling; + if (parent && parent.localName == "toolbar") { + toolbarId = parent.id; + nextItemId = nextItem && nextItem.id; + } + } + saveLocation(id, toolbarId, nextItemId); + } +} + +// Global window observer +function browserWindowObserver(handlers) { + this.handlers = handlers; +} + +browserWindowObserver.prototype = { + observe: function(aSubject, aTopic, aData) { + if (aTopic == "domwindowopened") { + aSubject.QueryInterface(Ci.nsIDOMWindow).addEventListener("load", this, false); + } else if (aTopic == "domwindowclosed") { + if (aSubject.document.documentElement.getAttribute("windowtype") == "navigator:browser") { + this.handlers.onShutdown(aSubject); + } + } + }, + + handleEvent: function(aEvent) { + let aWindow = aEvent.currentTarget; + aWindow.removeEventListener(aEvent.type, this, false); + + if (aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser") { + this.handlers.onStartup(aWindow); + } + } +}; + +// Run on every window startup +function browserWindowStartup(aWindow) { + for (let [id, onBuild] of buttonsList) { + insertButton(aWindow, id, onBuild); + } + aWindow.addEventListener("aftercustomization", afterCustomize, false); +}; + +// Run on every window shutdown +function browserWindowShutdown(aWindow) { + for (let [id, onBuild] of buttonsList) { + removeButton(aWindow, id); + } + aWindow.removeEventListener("aftercustomization", afterCustomize, false); +} + +// Main object +const buttons = { + createButton: function(aProperties) { + // If no buttons were inserted yet, setup global window observer + if (buttonsList.size == 0) { + let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher); + gWindowListener = new browserWindowObserver({ + onStartup: browserWindowStartup, + onShutdown: browserWindowShutdown + }); + ww.registerNotification(gWindowListener); + } + + // Add button to list + buttonsList.set(aProperties.id, aProperties.onBuild); + + // Inster button to all open windows + let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); + let winenu = wm.getEnumerator("navigator:browser"); + while (winenu.hasMoreElements()) { + let win = winenu.getNext(); + insertButton(win, aProperties.id, aProperties.onBuild); + // When first button inserted, add afterCustomize listener + if (buttonsList.size == 1) { + win.addEventListener("aftercustomization", afterCustomize, false); + } + } + }, + + destroyButton: function(id) { + // Remove button from list + buttonsList.delete(id); + + // If no more buttons exist, remove global window observer + if (buttonsList.size == 0) { + let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher); + ww.unregisterNotification(gWindowListener); + gWindowListener = null; + } + + // Remove button from all open windows + let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); + let winenu = wm.getEnumerator("navigator:browser"); + while (winenu.hasMoreElements()) { + let win = winenu.getNext(); + removeButton(win, id); + // If no more buttons exist, remove afterCustomize listener + if (buttonsList.size == 0) { + win.removeEventListener("aftercustomization", afterCustomize, false); + } + } + }, + + getNode: function(id, window) { + return window[id]; + } +}; + +exports.buttons = buttons; diff --git a/toolkit/jetpack/sdk/ui/state.js b/toolkit/jetpack/sdk/ui/state.js index 152ce696d..c90d4283d 100644 --- a/toolkit/jetpack/sdk/ui/state.js +++ b/toolkit/jetpack/sdk/ui/state.js @@ -8,6 +8,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '*', 'Firefox': '*', 'SeaMonkey': '*', 'Thunderbird': '*' diff --git a/toolkit/jetpack/sdk/ui/state/events.js b/toolkit/jetpack/sdk/ui/state/events.js index 98909656a..34d14be3a 100644 --- a/toolkit/jetpack/sdk/ui/state/events.js +++ b/toolkit/jetpack/sdk/ui/state/events.js @@ -7,6 +7,7 @@ module.metadata = { 'stability': 'experimental', 'engines': { + 'Palemoon': '*', 'Firefox': '*', 'SeaMonkey': '*', 'Thunderbird': '*' |